Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 72dd8b38dccb2ecde8f589ce13b7d69352250e3f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
/*******************************************************************************
 * Copyright (c) 2006, 2016 Wind River Systems and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Wind River Systems - initial API and implementation
 *     Ericsson	AB		  - Modified for handling of multiple threads
 *     Indel AG           - [369622] fixed moveToLine using MinGW
 *     Marc Khouzam (Ericsson) - Support for operations on multiple execution contexts (bug 330974)
 *     Alvaro Sanchez-Leon (Ericsson AB) - Support for Step into selection (bug 244865)
 *     Alvaro Sanchez-Leon (Ericsson AB) - Bug 415362
 *     Marc Khouzam (Ericsson) - Wait for *stopped event when suspending (bug 429621)
 *     Xavier Raynaud (Kalray) - Bug 438635
 *******************************************************************************/

package org.eclipse.cdt.dsf.gdb.service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.cdt.core.IAddress;
import org.eclipse.cdt.core.model.IFunctionDeclaration;
import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.DsfRunnable;
import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants;
import org.eclipse.cdt.dsf.concurrent.ImmediateCountingRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.ImmediateDataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.Immutable;
import org.eclipse.cdt.dsf.concurrent.MultiRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.concurrent.Sequence;
import org.eclipse.cdt.dsf.concurrent.Sequence.Step;
import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent;
import org.eclipse.cdt.dsf.datamodel.DMContexts;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.datamodel.IDMEvent;
import org.eclipse.cdt.dsf.debug.service.IBreakpoints;
import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointDMContext;
import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext;
import org.eclipse.cdt.dsf.debug.service.IBreakpointsExtension.IBreakpointHitDMEvent;
import org.eclipse.cdt.dsf.debug.service.ICachingService;
import org.eclipse.cdt.dsf.debug.service.IMultiRunControl;
import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext;
import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMContext;
import org.eclipse.cdt.dsf.debug.service.IRunControl;
import org.eclipse.cdt.dsf.debug.service.IRunControl2;
import org.eclipse.cdt.dsf.debug.service.IRunControl3;
import org.eclipse.cdt.dsf.debug.service.ISourceLookup;
import org.eclipse.cdt.dsf.debug.service.ISourceLookup.ISourceLookupDMContext;
import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext;
import org.eclipse.cdt.dsf.debug.service.command.ICommand;
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService;
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlShutdownDMEvent;
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
import org.eclipse.cdt.dsf.gdb.internal.service.command.events.MITracepointSelectedEvent;
import org.eclipse.cdt.dsf.gdb.internal.service.control.StepIntoSelectionActiveOperation;
import org.eclipse.cdt.dsf.gdb.internal.service.control.StepIntoSelectionUtils;
import org.eclipse.cdt.dsf.mi.service.IMIBreakpointPathAdjuster;
import org.eclipse.cdt.dsf.mi.service.IMICommandControl;
import org.eclipse.cdt.dsf.mi.service.IMIContainerDMContext;
import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext;
import org.eclipse.cdt.dsf.mi.service.IMIProcesses;
import org.eclipse.cdt.dsf.mi.service.IMIRunControl;
import org.eclipse.cdt.dsf.mi.service.MIBreakpointDMData;
import org.eclipse.cdt.dsf.mi.service.MIBreakpoints;
import org.eclipse.cdt.dsf.mi.service.MIBreakpoints.MIBreakpointDMContext;
import org.eclipse.cdt.dsf.mi.service.MIRunControl;
import org.eclipse.cdt.dsf.mi.service.MIStack;
import org.eclipse.cdt.dsf.mi.service.command.CommandFactory;
import org.eclipse.cdt.dsf.mi.service.command.events.IMIDMEvent;
import org.eclipse.cdt.dsf.mi.service.command.events.MIBreakpointHitEvent;
import org.eclipse.cdt.dsf.mi.service.command.events.MICatchpointHitEvent;
import org.eclipse.cdt.dsf.mi.service.command.events.MIErrorEvent;
import org.eclipse.cdt.dsf.mi.service.command.events.MIEvent;
import org.eclipse.cdt.dsf.mi.service.command.events.MIFunctionFinishedEvent;
import org.eclipse.cdt.dsf.mi.service.command.events.MIInferiorExitEvent;
import org.eclipse.cdt.dsf.mi.service.command.events.MIRunningEvent;
import org.eclipse.cdt.dsf.mi.service.command.events.MISharedLibEvent;
import org.eclipse.cdt.dsf.mi.service.command.events.MISignalEvent;
import org.eclipse.cdt.dsf.mi.service.command.events.MISteppingRangeEvent;
import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent;
import org.eclipse.cdt.dsf.mi.service.command.events.MIThreadCreatedEvent;
import org.eclipse.cdt.dsf.mi.service.command.events.MIThreadExitEvent;
import org.eclipse.cdt.dsf.mi.service.command.events.MIWatchpointTriggerEvent;
import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakInsertInfo;
import org.eclipse.cdt.dsf.mi.service.command.output.MIFrame;
import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo;
import org.eclipse.cdt.dsf.mi.service.command.output.MIStackInfoDepthInfo;
import org.eclipse.cdt.dsf.mi.service.command.output.MIThread;
import org.eclipse.cdt.dsf.mi.service.command.output.MIThreadInfoInfo;
import org.eclipse.cdt.dsf.service.AbstractDsfService;
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.core.DebugException;
import org.osgi.framework.BundleContext;

/**
 * Implementation note: This class implements event handlers for the events that
 * are generated by this service itself. When the event is dispatched, these
 * handlers will be called first, before any of the clients. These handlers
 * update the service's internal state information to make them consistent with
 * the events being issued. Doing this in the handlers as opposed to when the
 * events are generated, guarantees that the state of the service will always be
 * consistent with the events. The purpose of this pattern is to allow clients
 * that listen to service events and track service state, to be perfectly in
 * sync with the service state.
 * @since 1.1
 */
public class GDBRunControl_7_0_NS extends AbstractDsfService
		implements IMIRunControl, IMultiRunControl, ICachingService, IRunControl3 {
	// /////////////////////////////////////////////////////////////////////////
	// CONSTANTS
	// /////////////////////////////////////////////////////////////////////////

	@Immutable
	private static class ExecutionData implements IExecutionDMData2 {
		private final StateChangeReason fReason;
		private final String fDetails;

		ExecutionData(StateChangeReason reason, String details) {
			fReason = reason;
			fDetails = details;
		}

		@Override
		public StateChangeReason getStateChangeReason() {
			return fReason;
		}

		@Override
		public String getDetails() {
			return fDetails;
		}
	}

	/**
	 * Base class for events generated by the MI Run Control service.  Most events
	 * generated by the MI Run Control service are directly caused by some MI event.
	 * Other services may need access to the extended MI data carried in the event.
	 *
	 * @param <V> DMC that this event refers to
	 * @param <T> MIInfo object that is the direct cause of this event
	 * @see MIRunControl
	 */
	@Immutable
	private static class RunControlEvent<V extends IDMContext, T extends MIEvent<? extends IDMContext>>
			extends AbstractDMEvent<V> implements IMIDMEvent {
		final private T fMIInfo;

		public RunControlEvent(V dmc, T miInfo) {
			super(dmc);
			fMIInfo = miInfo;
		}

		@Override
		public T getMIEvent() {
			return fMIInfo;
		}
	}

	/**
	 * Indicates that the given thread has been suspended.
	 * @since 4.0
	 */
	@Immutable
	protected static class SuspendedEvent extends RunControlEvent<IExecutionDMContext, MIStoppedEvent>
			implements ISuspendedDMEvent {
		SuspendedEvent(IExecutionDMContext ctx, MIStoppedEvent miInfo) {
			super(ctx, miInfo);
		}

		@Override
		public StateChangeReason getReason() {
			if (getMIEvent() instanceof MICatchpointHitEvent) { // must precede MIBreakpointHitEvent
				return StateChangeReason.EVENT_BREAKPOINT;
			} else if (getMIEvent() instanceof MITracepointSelectedEvent) { // must precede MIBreakpointHitEvent
				return StateChangeReason.UNKNOWN; // Don't display anything here, the details will take care of it
			} else if (getMIEvent() instanceof MIBreakpointHitEvent) {
				return StateChangeReason.BREAKPOINT;
			} else if (getMIEvent() instanceof MISteppingRangeEvent) {
				return StateChangeReason.STEP;
			} else if (getMIEvent() instanceof MIFunctionFinishedEvent) {
				return StateChangeReason.STEP;
			} else if (getMIEvent() instanceof MISharedLibEvent) {
				return StateChangeReason.SHAREDLIB;
			} else if (getMIEvent() instanceof MISignalEvent) {
				return StateChangeReason.SIGNAL;
			} else if (getMIEvent() instanceof MIWatchpointTriggerEvent) {
				return StateChangeReason.WATCHPOINT;
			} else if (getMIEvent() instanceof MIErrorEvent) {
				return StateChangeReason.ERROR;
			} else {
				return StateChangeReason.USER_REQUEST;
			}
		}

		public String getDetails() {
			MIStoppedEvent event = getMIEvent();
			if (event instanceof MICatchpointHitEvent) { // must precede MIBreakpointHitEvent
				return ((MICatchpointHitEvent) event).getReason();
			} else if (event instanceof MITracepointSelectedEvent) { // must precede MIBreakpointHitEvent
				return ((MITracepointSelectedEvent) event).getReason();
			} else if (event instanceof MISharedLibEvent) {
				return ((MISharedLibEvent) event).getLibrary();
			} else if (event instanceof MISignalEvent) {
				return ((MISignalEvent) event).getName() + ':' + ((MISignalEvent) event).getMeaning();
			} else if (event instanceof MIWatchpointTriggerEvent) {
				return ((MIWatchpointTriggerEvent) event).getExpression();
			} else if (event instanceof MIErrorEvent) {
				return ((MIErrorEvent) event).getMessage();
			}

			return null;
		}
	}

	/**
	 * Indicates that the given thread has been suspended on a breakpoint.
	 * @since 4.0
	 */
	@Immutable
	protected static class BreakpointHitEvent extends SuspendedEvent implements IBreakpointHitDMEvent {
		final private IBreakpointDMContext[] fBreakpoints;

		BreakpointHitEvent(IExecutionDMContext ctx, MIBreakpointHitEvent miInfo, IBreakpointDMContext bpCtx) {
			super(ctx, miInfo);

			fBreakpoints = new IBreakpointDMContext[] { bpCtx };
		}

		@Override
		public IBreakpointDMContext[] getBreakpoints() {
			return fBreakpoints;
		}
	}

	/**
	 * @since 4.0
	 */
	@Immutable
	protected static class ResumedEvent extends RunControlEvent<IExecutionDMContext, MIRunningEvent>
			implements IResumedDMEvent {
		ResumedEvent(IExecutionDMContext ctx, MIRunningEvent miInfo) {
			super(ctx, miInfo);
		}

		@Override
		public StateChangeReason getReason() {
			if (getMIEvent() != null) {
				switch (getMIEvent().getType()) {
				case MIRunningEvent.CONTINUE:
					return StateChangeReason.USER_REQUEST;
				case MIRunningEvent.NEXT:
				case MIRunningEvent.NEXTI:
					return StateChangeReason.STEP;
				case MIRunningEvent.STEP:
				case MIRunningEvent.STEPI:
					return StateChangeReason.STEP;
				case MIRunningEvent.FINISH:
					return StateChangeReason.STEP;
				case MIRunningEvent.UNTIL:
				case MIRunningEvent.RETURN:
					break;
				}
			}
			return StateChangeReason.UNKNOWN;
		}
	}

	/**
	 * @since 4.0
	 */
	@Immutable
	protected static class StartedDMEvent extends RunControlEvent<IExecutionDMContext, MIThreadCreatedEvent>
			implements IStartedDMEvent {
		StartedDMEvent(IMIExecutionDMContext executionDmc, MIThreadCreatedEvent miInfo) {
			super(executionDmc, miInfo);
		}
	}

	/**
	 * @since 4.0
	 */
	@Immutable
	protected static class ExitedDMEvent extends RunControlEvent<IExecutionDMContext, MIThreadExitEvent>
			implements IExitedDMEvent {
		ExitedDMEvent(IMIExecutionDMContext executionDmc, MIThreadExitEvent miInfo) {
			super(executionDmc, miInfo);
		}
	}

	protected class MIThreadRunState {
		// State flags
		boolean fSuspended = false;
		boolean fResumePending = false;
		boolean fStepping = false;
		RunControlEvent<IExecutionDMContext, ?> fLatestEvent = null;

		/**
		 * What caused the state change. E.g., a signal was thrown.
		 */
		StateChangeReason fStateChangeReason;

		/**
		 * Further detail on what caused the state change. E.g., the specific signal
		 * that was throw was a SIGINT. The exact string comes from gdb in the mi
		 * event. May be null, as not all types of state change have additional
		 * detail of interest.
		 */
		String fStateChangeDetails;
	}

	/**
	 * @since 4.0
	 */
	protected static class RunToLineActiveOperation {
		private IMIExecutionDMContext fThreadContext;
		private String fBpId;
		private String fFileLocation;
		private String fAddrLocation;
		private boolean fSkipBreakpoints;

		/** @since 5.0 */
		public RunToLineActiveOperation(IMIExecutionDMContext threadContext, String bpId, String fileLoc, String addr,
				boolean skipBreakpoints) {
			fThreadContext = threadContext;
			fBpId = bpId;
			fFileLocation = fileLoc;
			fAddrLocation = addr;
			fSkipBreakpoints = skipBreakpoints;
		}

		public IMIExecutionDMContext getThreadContext() {
			return fThreadContext;
		}

		/** @since 5.0 */
		public String getBreakpointId() {
			return fBpId;
		}

		public String getFileLocation() {
			return fFileLocation;
		}

		public String getAddrLocation() {
			return fAddrLocation;
		}

		public boolean shouldSkipBreakpoints() {
			return fSkipBreakpoints;
		}
	}

	// /////////////////////////////////////////////////////////////////////////
	// MIRunControlNS
	///////////////////////////////////////////////////////////////////////////

	private ICommandControlService fConnection;
	private CommandFactory fCommandFactory;
	private IGDBProcesses fProcessService;

	private boolean fTerminated = false;

	// ThreadStates indexed by the execution context
	protected Map<IMIExecutionDMContext, MIThreadRunState> fThreadRunStates = new HashMap<>();

	private RunToLineActiveOperation fRunToLineActiveOperation = null;

	private StepIntoSelectionActiveOperation fStepInToSelectionActiveOperation = null;

	/** @since 4.0 */
	protected RunToLineActiveOperation getRunToLineActiveOperation() {
		return fRunToLineActiveOperation;
	}

	/** @since 4.0 */
	protected void setRunToLineActiveOperation(RunToLineActiveOperation operation) {
		fRunToLineActiveOperation = operation;
	}

	/**
	 * Set of threads for which the next MIRunning event should be silenced.
	 */
	private Set<IMIExecutionDMContext> fDisableNextRunningEventDmcSet = new HashSet<>();
	/**
	 * Set of threads for which the next MISignal (MIStopped) event should be silenced.
	 */
	private Set<IMIExecutionDMContext> fDisableNextSignalEventDmcSet = new HashSet<>();
	/**
	 * Map that stores the silenced MIStopped event for the specified thread, in case we need to use it for a failure.
	 */
	private Map<IMIExecutionDMContext, MIStoppedEvent> fSilencedSignalEventMap = new HashMap<>();

	/**
	 * This variable allows us to know if run control operation
	 * should be enabled or disabled.  Run control operations are
	 * always enabled except when visualizing tracepoints.
	 */
	private boolean fRunControlOperationsEnabled = true;

	///////////////////////////////////////////////////////////////////////////
	// Initialization and shutdown
	///////////////////////////////////////////////////////////////////////////

	public GDBRunControl_7_0_NS(DsfSession session) {
		super(session);
	}

	@Override
	public void initialize(final RequestMonitor rm) {
		super.initialize(new ImmediateRequestMonitor(rm) {
			@Override
			protected void handleSuccess() {
				doInitialize(rm);
			}
		});
	}

	private void doInitialize(final RequestMonitor rm) {
		register(
				new String[] { IRunControl.class.getName(), IRunControl2.class.getName(), IMIRunControl.class.getName(),
						IMultiRunControl.class.getName(), IRunControl3.class.getName() },
				new Hashtable<String, String>());
		fConnection = getServicesTracker().getService(ICommandControlService.class);
		fCommandFactory = getServicesTracker().getService(IMICommandControl.class).getCommandFactory();
		fProcessService = getServicesTracker().getService(IGDBProcesses.class);

		getSession().addServiceEventListener(this, null);
		rm.done();
	}

	@Override
	public void shutdown(final RequestMonitor rm) {
		unregister();
		getSession().removeServiceEventListener(this);
		super.shutdown(rm);
	}

	/** @since 4.1 */
	protected boolean getRunControlOperationsEnabled() {
		return fRunControlOperationsEnabled;
	}

	/** @since 4.1 */
	protected void setRunControlOperationsEnabled(boolean runControlEnabled) {
		fRunControlOperationsEnabled = runControlEnabled;
	}

	///////////////////////////////////////////////////////////////////////////
	// AbstractDsfService
	///////////////////////////////////////////////////////////////////////////

	@Override
	protected BundleContext getBundleContext() {
		return GdbPlugin.getBundleContext();
	}

	///////////////////////////////////////////////////////////////////////////
	// IRunControl
	///////////////////////////////////////////////////////////////////////////

	// ------------------------------------------------------------------------
	// Suspend
	// ------------------------------------------------------------------------

	@Override
	public boolean isSuspended(IExecutionDMContext context) {

		// Thread case
		if (context instanceof IMIExecutionDMContext) {
			MIThreadRunState threadState = fThreadRunStates.get(context);
			return (threadState == null) ? false : !fTerminated && threadState.fSuspended;
		}

		// Process case.  The process is considered suspended as long
		// as one of its thread is suspended
		if (context instanceof IMIContainerDMContext) {
			boolean hasThread = false;
			for (IMIExecutionDMContext threadContext : fThreadRunStates.keySet()) {
				if (DMContexts.isAncestorOf(threadContext, context)) {
					hasThread = true;
					if (isSuspended(threadContext))
						return true;
				}
			}
			// If this container does not have any threads, it means it wasn't started
			// yet or it was terminated, so we can consider it suspended
			if (hasThread == false)
				return true;
		}

		// Default case
		return false;
	}

	@Override
	public void canSuspend(IExecutionDMContext context, DataRequestMonitor<Boolean> rm) {
		if (fRunControlOperationsEnabled == false) {
			rm.done(false);
			return;
		}

		rm.done(doCanSuspend(context));
	}

	/**
	 * @since 4.5
	 */
	protected boolean doCanSuspend(IExecutionDMContext context) {
		// Thread case
		if (context instanceof IMIExecutionDMContext) {
			MIThreadRunState threadState = fThreadRunStates.get(context);
			return (threadState == null) ? false : !fTerminated && !threadState.fSuspended;
		}

		// Process case
		if (context instanceof IMIContainerDMContext) {
			for (IMIExecutionDMContext threadContext : fThreadRunStates.keySet()) {
				if (DMContexts.isAncestorOf(threadContext, context)) {
					if (doCanSuspend(threadContext)) {
						return true;
					}
				}
			}
			return false;
		}

		// Default case
		return false;
	}

	@Override
	public void suspend(IExecutionDMContext context, final RequestMonitor rm) {

		assert context != null;

		// Thread case
		IMIExecutionDMContext thread = DMContexts.getAncestorOfType(context, IMIExecutionDMContext.class);
		if (thread != null) {
			doSuspend(thread, rm);
			return;
		}

		// Process case
		IMIContainerDMContext container = DMContexts.getAncestorOfType(context, IMIContainerDMContext.class);
		if (container != null) {
			doSuspend(container, rm);
			return;
		}

		// Default case
		rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Invalid context type.", null)); //$NON-NLS-1$
		rm.done();
	}

	/**
	 * Request the suspend for a single thread and wait for a proper *stopped event before
	 * indicating success.
	 */
	private void doSuspend(final IMIExecutionDMContext context, final RequestMonitor rm) {
		if (!doCanSuspend(context)) {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED,
					"Given context: " + context + ", is already suspended.", null)); //$NON-NLS-1$ //$NON-NLS-2$
			rm.done();
			return;
		}

		// Start the job before sending the interrupt command
		// to make sure we don't miss the *stopped event
		final MonitorSuspendJob monitorJob = new MonitorSuspendJob(context, 0, rm);
		fConnection.queueCommand(fCommandFactory.createMIExecInterrupt(context),
				new ImmediateDataRequestMonitor<MIInfo>() {
					@Override
					protected void handleSuccess() {
						// Nothing to do in the case of success, the monitoring job
						// will take care of completing the RM once it gets the
						// *stopped event.
					}

					@Override
					protected void handleFailure() {
						// In case of failure, we must cancel the monitoring job
						// and indicate the failure in the rm.
						monitorJob.cleanAndCancel();
						rm.done(getStatus());
					}
				});
	}

	/**
	 * Request the suspend for a process.  In this case we don't wait for any *stopped events explicitly
	 * because we would need to wait for one per thread and manage all those events.  It is not necessary.
	 * @since 4.5
	 */
	protected void doSuspend(IMIContainerDMContext context, final RequestMonitor rm) {
		if (!doCanSuspend(context)) {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED,
					"Given context: " + context + ", is already suspended.", null)); //$NON-NLS-1$ //$NON-NLS-2$
			rm.done();
			return;
		}

		String groupId = context.getGroupId();
		fConnection.queueCommand(fCommandFactory.createMIExecInterrupt(context, groupId),
				new DataRequestMonitor<MIInfo>(getExecutor(), rm));
	}

	/**
	 * Job that waits for a *stopped event after a suspend operation on a thread.
	 *
	 * If the suspend operation receives its corresponding *stopped event in time,
	 * the job will mark the RM with a success status.  If the event is not received
	 * before the timeout, the job will fail the request monitor.
	 *
	 * @since 4.5
	 */
	protected class MonitorSuspendJob extends Job {
		// Bug 310274.  Until we have a preference to configure timeouts,
		// we need a large enough default timeout to accommodate slow
		// remote sessions.
		private final static int TIMEOUT_DEFAULT_VALUE = 5000;

		private final RequestMonitor fRequestMonitor;
		private final IMIExecutionDMContext fThread;

		public MonitorSuspendJob(IMIExecutionDMContext dmc, int timeout, RequestMonitor rm) {
			super("Suspend monitor job."); //$NON-NLS-1$
			setSystem(true);
			fThread = dmc;
			fRequestMonitor = rm;

			if (timeout <= 0) {
				timeout = TIMEOUT_DEFAULT_VALUE; // default of 5 seconds
			}

			// Register to listen for the stopped event
			getSession().addServiceEventListener(this, null);

			schedule(timeout);
		}

		/**
		 * Cleanup job and cancel it.
		 * This method is required because super.canceling() is only called
		 * if the job is actually running.
		 */
		public boolean cleanAndCancel() {
			if (getExecutor().isInExecutorThread()) {
				getSession().removeServiceEventListener(this);
			} else {
				getExecutor().submit(new DsfRunnable() {
					@Override
					public void run() {
						getSession().removeServiceEventListener(MonitorSuspendJob.this);
					}
				});
			}
			return cancel();
		}

		@DsfServiceEventHandler
		public void eventDispatched(MIStoppedEvent e) {
			if (fThread.equals(e.getDMContext())) {
				// The thread we were waiting for did stop
				if (cleanAndCancel()) {
					fRequestMonitor.done();
				}
			}
		}

		@Override
		protected IStatus run(IProgressMonitor monitor) {
			// This will be called when the timeout is hit and no *stopped event was received
			getExecutor().submit(new DsfRunnable() {
				@Override
				public void run() {
					getSession().removeServiceEventListener(MonitorSuspendJob.this);
					fRequestMonitor.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID,
							IDsfStatusConstants.REQUEST_FAILED, "Suspend operation timeout.", null)); //$NON-NLS-1$
				}
			});
			return Status.OK_STATUS;
		}
	}

	// ------------------------------------------------------------------------
	// Resume
	// ------------------------------------------------------------------------

	@Override
	public void canResume(IExecutionDMContext context, DataRequestMonitor<Boolean> rm) {
		if (fRunControlOperationsEnabled == false) {
			rm.done(false);
			return;
		}

		rm.done(doCanResume(context));
	}

	/** @since 5.0 */
	protected boolean doCanResume(IExecutionDMContext context) {
		// Thread case
		if (context instanceof IMIExecutionDMContext) {
			MIThreadRunState threadState = fThreadRunStates.get(context);
			return (threadState == null) ? false
					: !fTerminated && threadState.fSuspended && !threadState.fResumePending;
		}

		// Process case
		if (context instanceof IMIContainerDMContext) {
			for (IMIExecutionDMContext threadContext : fThreadRunStates.keySet()) {
				if (DMContexts.isAncestorOf(threadContext, context)) {
					if (doCanResume(threadContext)) {
						return true;
					}
				}
			}
			return false;
		}

		// Default case
		return false;
	}

	@Override
	public void resume(IExecutionDMContext context, final RequestMonitor rm) {

		assert context != null;

		// Thread case
		IMIExecutionDMContext thread = DMContexts.getAncestorOfType(context, IMIExecutionDMContext.class);
		if (thread != null) {
			doResume(thread, rm);
			return;
		}

		// Container case
		IMIContainerDMContext container = DMContexts.getAncestorOfType(context, IMIContainerDMContext.class);
		if (container != null) {
			doResume(container, rm);
			return;
		}

		// Default case
		rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Invalid context type.", null)); //$NON-NLS-1$
		rm.done();
	}

	private void doResume(IMIExecutionDMContext context, final RequestMonitor rm) {
		if (!doCanResume(context)) {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE,
					"Given context: " + context + ", is already running.", null)); //$NON-NLS-1$ //$NON-NLS-2$
			rm.done();
			return;
		}

		final MIThreadRunState threadState = fThreadRunStates.get(context);
		if (threadState == null) {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE,
					"Given context: " + context + " can't be found.", null)); //$NON-NLS-1$ //$NON-NLS-2$
			rm.done();
			return;
		}

		threadState.fResumePending = true;
		fConnection.queueCommand(fCommandFactory.createMIExecContinue(context),
				new DataRequestMonitor<MIInfo>(getExecutor(), rm) {
					@Override
					protected void handleFailure() {
						threadState.fResumePending = false;
						super.handleFailure();
					}
				});
	}

	/** @since 5.0 */
	protected void doResume(IMIContainerDMContext context, final RequestMonitor rm) {
		if (!doCanResume(context)) {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE,
					"Given context: " + context + ", is already running.", null)); //$NON-NLS-1$ //$NON-NLS-2$
			rm.done();
			return;
		}

		String groupId = context.getGroupId();
		fConnection.queueCommand(fCommandFactory.createMIExecContinue(context, groupId),
				new DataRequestMonitor<MIInfo>(getExecutor(), rm));
	}

	// ------------------------------------------------------------------------
	// Step
	// ------------------------------------------------------------------------

	@Override
	public boolean isStepping(IExecutionDMContext context) {

		// If it's a thread, just look it up
		if (context instanceof IMIExecutionDMContext) {
			MIThreadRunState threadState = fThreadRunStates.get(context);
			return (threadState == null) ? false : !fTerminated && threadState.fStepping;
		}

		// Default case
		return false;
	}

	@Override
	public void canStep(final IExecutionDMContext context, StepType stepType, final DataRequestMonitor<Boolean> rm) {
		if (fRunControlOperationsEnabled == false) {
			rm.done(false);
			return;
		}

		// If it's a thread, just look it up
		if (context instanceof IMIExecutionDMContext) {
			if (stepType == StepType.STEP_RETURN) {
				// A step return will always be done in the top stack frame.
				// If the top stack frame is the only stack frame, it does not make sense
				// to do a step return since GDB will reject it.
				MIStack stackService = getServicesTracker().getService(MIStack.class);
				if (stackService != null) {
					// Check that the stack is at least two deep.
					stackService.getStackDepth(context, 2, new DataRequestMonitor<Integer>(getExecutor(), rm) {
						@Override
						public void handleCompleted() {
							if (isSuccess() && getData() == 1) {
								rm.done(false);
							} else {
								canResume(context, rm);
							}
						}
					});
					return;
				}
			}

			canResume(context, rm);
			return;
		}

		// If it's a container, then we don't want to step it
		rm.done(false);
	}

	@Override
	public void step(IExecutionDMContext context, StepType stepType, final RequestMonitor rm) {
		step(context, stepType, true, rm);
	}

	private void step(IExecutionDMContext context, StepType stepType, boolean checkCanResume, final RequestMonitor rm) {

		assert context != null;

		IMIExecutionDMContext dmc = DMContexts.getAncestorOfType(context, IMIExecutionDMContext.class);
		if (dmc == null) {
			rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR,
					"Given context: " + context + " is not an MI execution context.", null)); //$NON-NLS-1$ //$NON-NLS-2$
			return;
		}

		if (checkCanResume && !doCanResume(dmc)) {
			rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Cannot resume context", null)); //$NON-NLS-1$
			return;
		}

		final MIThreadRunState threadState = fThreadRunStates.get(context);
		if (threadState == null) {
			rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE,
					"Given context: " + context + " can't be found.", null)); //$NON-NLS-1$ //$NON-NLS-2$
			return;
		}

		ICommand<MIInfo> cmd = null;
		switch (stepType) {
		case STEP_INTO:
			cmd = fCommandFactory.createMIExecStep(dmc);
			break;
		case STEP_OVER:
			cmd = fCommandFactory.createMIExecNext(dmc);
			break;
		case STEP_RETURN:
			// The -exec-finish command operates on the selected stack frame, but here we always
			// want it to operate on the stop stack frame. So we manually create a top-frame
			// context to use with the MI command.
			// We get a local instance of the stack service because the stack service can be shut
			// down before the run control service is shut down. So it is possible for the
			// getService() request below to return null.
			MIStack stackService = getServicesTracker().getService(MIStack.class);
			if (stackService != null) {
				IFrameDMContext topFrameDmc = stackService.createFrameDMContext(dmc, 0);
				cmd = fCommandFactory.createMIExecFinish(topFrameDmc);
			} else {
				rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED,
						"Cannot create context for command, stack service not available.", null)); //$NON-NLS-1$
				return;
			}
			break;
		case INSTRUCTION_STEP_INTO:
			cmd = fCommandFactory.createMIExecStepInstruction(dmc);
			break;
		case INSTRUCTION_STEP_OVER:
			cmd = fCommandFactory.createMIExecNextInstruction(dmc);
			break;
		default:
			rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Given step type not supported", //$NON-NLS-1$
					null));
			return;
		}

		threadState.fResumePending = true;
		threadState.fStepping = true;
		fConnection.queueCommand(cmd, new DataRequestMonitor<MIInfo>(getExecutor(), rm) {
			@Override
			public void handleFailure() {
				threadState.fResumePending = false;
				threadState.fStepping = false;

				super.handleFailure();
			}
		});
	}

	// ------------------------------------------------------------------------
	// Run to line
	// ------------------------------------------------------------------------

	private void runToLocation(final IExecutionDMContext context, final String location, final boolean skipBreakpoints,
			final RequestMonitor rm) {

		assert context != null;

		final IMIExecutionDMContext dmc = DMContexts.getAncestorOfType(context, IMIExecutionDMContext.class);
		if (dmc == null) {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR,
					"Given context: " + context + " is not an MI execution context.", null)); //$NON-NLS-1$ //$NON-NLS-2$
			rm.done();
			return;
		}

		if (!doCanResume(dmc)) {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Cannot resume context", null)); //$NON-NLS-1$
			rm.done();
			return;
		}

		MIThreadRunState threadState = fThreadRunStates.get(dmc);
		if (threadState == null) {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE,
					"Given context: " + context + " can't be found.", null)); //$NON-NLS-1$ //$NON-NLS-2$
			rm.done();
			return;
		}

		IBreakpointsTargetDMContext bpDmc = DMContexts.getAncestorOfType(context, IBreakpointsTargetDMContext.class);
		fConnection.queueCommand(
				fCommandFactory.createMIBreakInsert(bpDmc, true, false, null, 0, location, dmc.getThreadId()),
				new DataRequestMonitor<MIBreakInsertInfo>(getExecutor(), rm) {
					@Override
					public void handleSuccess() {
						// We must set are RunToLineActiveOperation *before* we do the resume
						// or else we may get the stopped event, before we have set this variable.
						String bpId = getData().getMIBreakpoints()[0].getNumber();
						String addr = getData().getMIBreakpoints()[0].getAddress();
						fRunToLineActiveOperation = new RunToLineActiveOperation(dmc, bpId, location, addr,
								skipBreakpoints);

						resume(dmc, new RequestMonitor(getExecutor(), rm) {
							@Override
							public void handleFailure() {
								IBreakpointsTargetDMContext bpDmc = DMContexts.getAncestorOfType(
										fRunToLineActiveOperation.getThreadContext(),
										IBreakpointsTargetDMContext.class);
								String bpId = fRunToLineActiveOperation.getBreakpointId();

								fConnection.queueCommand(
										fCommandFactory.createMIBreakDelete(bpDmc, new String[] { bpId }),
										new DataRequestMonitor<MIInfo>(getExecutor(), null));
								fRunToLineActiveOperation = null;
								fStepInToSelectionActiveOperation = null;

								super.handleFailure();
							}
						});
					}
				});

	}

	// ------------------------------------------------------------------------
	// Step into Selection
	// ------------------------------------------------------------------------
	private void stepIntoSelection(final IExecutionDMContext context, final int baseLine, final String baseLineLocation,
			final boolean skipBreakpoints, final IFunctionDeclaration targetFunction, final RequestMonitor rm) {

		assert context != null;

		final IMIExecutionDMContext dmc = DMContexts.getAncestorOfType(context, IMIExecutionDMContext.class);
		if (dmc == null) {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR,
					"Given context: " + context + " is not an MI execution context.", null)); //$NON-NLS-1$ //$NON-NLS-2$
			rm.done();
			return;
		}

		if (!doCanResume(dmc)) {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Cannot resume context", null)); //$NON-NLS-1$
			rm.done();
			return;
		}

		MIThreadRunState threadState = fThreadRunStates.get(dmc);
		if (threadState == null) {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE,
					"Given context: " + context + " can't be found.", null)); //$NON-NLS-1$ //$NON-NLS-2$
			rm.done();
			return;
		}

		if (threadState.fLatestEvent == null || !(threadState.fLatestEvent instanceof SuspendedEvent)) {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE,
					"Given context: " + context + " invalid suspended event.", null)); //$NON-NLS-1$ //$NON-NLS-2$
			rm.done();
			return;
		}

		SuspendedEvent suspendedEvent = (SuspendedEvent) threadState.fLatestEvent;
		final MIFrame currentFrame = suspendedEvent.getMIEvent().getFrame();
		if (currentFrame == null) {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE,
					"Given event: " + suspendedEvent + " invalid frame in suspended event.", null)); //$NON-NLS-1$ //$NON-NLS-2$
			rm.done();
			return;
		}

		getStackDepth(dmc, new DataRequestMonitor<Integer>(getExecutor(), rm) {
			@Override
			public void handleSuccess() {
				if (getData() != null) {
					final int framesSize = getData().intValue();

					// make sure the operation is removed upon
					// failure detection
					final RequestMonitor rms = new RequestMonitor(getExecutor(), rm) {
						@Override
						protected void handleFailure() {
							fStepInToSelectionActiveOperation = null;
							super.handleFailure();
						}
					};

					if ((currentFrame.getFile() + ":" + currentFrame.getLine()).endsWith(baseLineLocation)) { //$NON-NLS-1$
						// Save the step into selection information
						fStepInToSelectionActiveOperation = new StepIntoSelectionActiveOperation(dmc, baseLine,
								targetFunction, framesSize, currentFrame);
						// Ready to step into a function selected
						// within a current line
						step(dmc, StepType.STEP_INTO, rms);
					} else {
						// Save the step into selection information
						fStepInToSelectionActiveOperation = new StepIntoSelectionActiveOperation(dmc, baseLine,
								targetFunction, framesSize, null);
						// Pointing to a line different than the current line
						// Needs to RunToLine before stepping to the selection
						runToLocation(dmc, baseLineLocation, skipBreakpoints, rms);
					}
				} else {
					rm.done();
				}
			}
		});
	}

	// ------------------------------------------------------------------------
	// Resume at location
	// ------------------------------------------------------------------------
	private void resumeAtLocation(IExecutionDMContext context, String location, RequestMonitor rm) {
		assert context != null;

		final IMIExecutionDMContext dmc = DMContexts.getAncestorOfType(context, IMIExecutionDMContext.class);
		if (dmc == null) {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR,
					"Given context: " + context + " is not an MI execution context.", null)); //$NON-NLS-1$ //$NON-NLS-2$
			rm.done();
			return;
		}

		if (!doCanResume(dmc)) {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Cannot resume context", null)); //$NON-NLS-1$
			rm.done();
			return;
		}

		final MIThreadRunState threadState = fThreadRunStates.get(dmc);
		if (threadState == null) {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE,
					"Given context: " + context + " can't be found.", null)); //$NON-NLS-1$ //$NON-NLS-2$
			rm.done();
			return;
		}

		threadState.fResumePending = true;
		fConnection.queueCommand(fCommandFactory.createMIExecJump(dmc, location),
				new DataRequestMonitor<MIInfo>(getExecutor(), rm) {
					@Override
					protected void handleFailure() {
						threadState.fResumePending = false;
						super.handleFailure();
					}
				});
	}
	// ------------------------------------------------------------------------
	// Support functions
	// ------------------------------------------------------------------------

	@Override
	public void getExecutionContexts(final IContainerDMContext containerDmc,
			final DataRequestMonitor<IExecutionDMContext[]> rm) {
		IMIProcesses procService = getServicesTracker().getService(IMIProcesses.class);
		procService.getProcessesBeingDebugged(containerDmc, new DataRequestMonitor<IDMContext[]>(getExecutor(), rm) {
			@Override
			protected void handleSuccess() {
				if (getData() instanceof IExecutionDMContext[]) {
					rm.setData((IExecutionDMContext[]) getData());
				} else {
					rm.setStatus(
							new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid contexts", null)); //$NON-NLS-1$
				}
				rm.done();
			}
		});
	}

	@Override
	public void getExecutionData(IExecutionDMContext dmc, DataRequestMonitor<IExecutionDMData> rm) {
		MIThreadRunState threadState = fThreadRunStates.get(dmc);
		if (threadState == null) {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE,
					"Given context: " + dmc + " is not a recognized execution context.", null)); //$NON-NLS-1$ //$NON-NLS-2$
			rm.done();
			return;
		}

		if (dmc instanceof IMIExecutionDMContext) {
			rm.setData(new ExecutionData(threadState.fSuspended ? threadState.fStateChangeReason : null,
					threadState.fStateChangeDetails));
		} else {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE,
					"Given context: " + dmc + " is not a recognized execution context.", null)); //$NON-NLS-1$ //$NON-NLS-2$
		}
		rm.done();
	}

	private IMIExecutionDMContext createMIExecutionContext(IContainerDMContext container, String threadId) {
		IMIProcesses procService = getServicesTracker().getService(IMIProcesses.class);

		IProcessDMContext procDmc = DMContexts.getAncestorOfType(container, IProcessDMContext.class);

		IThreadDMContext threadDmc = null;
		if (procDmc != null) {
			// For now, reuse the threadId as the OSThreadId
			threadDmc = procService.createThreadContext(procDmc, threadId);
		}

		return procService.createExecutionContext(container, threadDmc, threadId);
	}

	private void updateThreadState(IMIExecutionDMContext context, ResumedEvent event) {
		StateChangeReason reason = event.getReason();
		boolean isStepping = reason.equals(StateChangeReason.STEP);
		MIThreadRunState threadState = fThreadRunStates.get(context);
		if (threadState == null) {
			threadState = new MIThreadRunState();
			fThreadRunStates.put(context, threadState);
		}
		threadState.fSuspended = false;
		threadState.fResumePending = false;
		threadState.fStateChangeReason = reason;
		threadState.fStateChangeDetails = null; // we have no details of interest for a resume
		threadState.fStepping = isStepping;
		threadState.fLatestEvent = event;
	}

	private void updateThreadState(IMIExecutionDMContext context, SuspendedEvent event) {
		StateChangeReason reason = event.getReason();
		MIThreadRunState threadState = fThreadRunStates.get(context);
		if (threadState == null) {
			threadState = new MIThreadRunState();
			fThreadRunStates.put(context, threadState);
		}
		threadState.fSuspended = true;
		threadState.fResumePending = false;
		threadState.fStepping = false;
		threadState.fStateChangeReason = reason;
		threadState.fStateChangeDetails = event.getDetails();
		threadState.fLatestEvent = event;
	}

	/* ******************************************************************************
	 * Section to support making operations even when the target is unavailable.
	 *
	 * Although one would expect to be able to make commands all the time when
	 * in non-stop mode, it turns out that GDB has trouble with some commands
	 * like breakpoints.  The safe way to do it is to make sure we have at least
	 * one thread suspended.
	 *
	 * Basically, we must make sure one thread is suspended before making
	 * certain operations (currently breakpoints).  If that is not the case, we must
	 * first suspend one thread, then perform the specified operations,
	 * and finally resume that thread..
	 * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=242943
	 * and https://bugs.eclipse.org/bugs/show_bug.cgi?id=282273
	 *
	 * Note that for multi-process, we need to interrupt all processes
	 * that share the same binary before doing a breakpoint operation on any of
	 * those processes.  For simplicity, the logic below interrupts one thread of
	 * every process being debugged, without differentiating on the executable.
	 * Although it may seem wasteful to interrupt all processes when not necessary,
	 * in truth it is not so much; when making a breakpoint operation in Eclipse, that
	 * operation is propagated to all processes anyway, so they will all need to be
	 * interrupted.  The case where we are wasteful is when we start or stop debugging
	 * a process (starting a process, attaching to one, auto-attaching to one,
	 * detaching from one, terminating one); in those cases, we only want to apply the
	 * breakpoint operation to that one process and any other using the same binary.
	 * The wastefulness is not such a big deal for that case, and is worth the simpler
	 * solution.
	 * Of course, it can always be improved later on.
	 * See http://bugs.eclipse.org/337893
	 * ******************************************************************************/

	/**
	 * Utility class to store the parameters of the executeWithTargetAvailable() operations.
	 * @since 4.0
	 */
	protected static class TargetAvailableOperationInfo {
		public IDMContext ctx;
		public Sequence.Step[] steps;
		public RequestMonitor rm;

		public TargetAvailableOperationInfo(IDMContext ctx, Step[] steps, RequestMonitor rm) {
			super();
			this.ctx = ctx;
			this.steps = steps;
			this.rm = rm;
		}
	}

	// The set of threads that we will actually be suspended to make the containers suspended.
	private Set<IMIExecutionDMContext> fExecutionDmcToSuspendSet = new HashSet<>();

	// Do we currently have an executeWithTargetAvailable() operation ongoing?
	private boolean fOngoingOperation;
	// Are we currently executing steps passed into executeWithTargetAvailable()?
	// This allows us to know if we can add more steps to execute or if we missed
	// our opportunity
	private boolean fCurrentlyExecutingSteps;

	// MultiRequestMonitor that allows us to track all the different steps we are
	// executing.  Once all steps are executed, we can complete this MultiRM and
	// allow the global sequence to continue.
	// Note that we couldn't use a CountingRequestMonitor because that type of RM
	// needs to know in advance how many subRms it will track; the MultiRM allows us
	// to receive more steps to execute continuously, and be able to update the MultiRM.
	private MultiRequestMonitor<RequestMonitor> fExecuteQueuedOpsStepMonitor;
	// The number of batches of steps that are still being executing for potentially
	// concurrent executeWithTargetAvailable() operations.
	// Once this gets to zero, we know we have executed all the steps we were aware of
	// and we can complete the operation.
	private int fNumStepsStillExecuting;
	// Queue of executeWithTargetAvailable() operations that need to be processed.
	private LinkedList<TargetAvailableOperationInfo> fOperationsPending = new LinkedList<>();

	/**
	 * Returns whether there is currently an ExecuteWithTargetAvailable() operation ongoing.
	 * @since 4.0
	 */
	protected boolean isTargetAvailableOperationOngoing() {
		return fOngoingOperation;
	}

	/** @since 4.0 */
	protected void setTargetAvailableOperationOngoing(boolean ongoing) {
		fOngoingOperation = ongoing;
	}

	/**
	 * Returns whether we are current in the process of executing the steps
	 * that were passed to ExecuteWithTargetAvailable().
	 * When this value is true, we can send more steps to be executed.
	 * @since 4.0
	 */
	protected boolean isCurrentlyExecutingSteps() {
		return fCurrentlyExecutingSteps;
	}

	/** @since 4.0 */
	protected void setCurrentlyExecutingSteps(boolean executing) {
		fCurrentlyExecutingSteps = executing;
	}

	/**
	 * Returns the requestMonitor that will be run once all steps sent to
	 * ExecuteWithTargetAvailable() have been executed.
	 * @since 4.0
	 */
	protected MultiRequestMonitor<RequestMonitor> getExecuteQueuedStepsRM() {
		return fExecuteQueuedOpsStepMonitor;
	}

	/** @since 4.0 */
	protected void setExecuteQueuedStepsRM(MultiRequestMonitor<RequestMonitor> rm) {
		fExecuteQueuedOpsStepMonitor = rm;
	}

	/**
	 * Returns the number of batches of steps sent to ExecuteWithTargetAvailable()
	 * that are still executing.  Once this number reaches zero, we can complete
	 * the overall ExecuteWithTargetAvailable() operation.
	 * @since 4.0
	 */
	protected int getNumStepsStillExecuting() {
		return fNumStepsStillExecuting;
	}

	/** @since 4.0 */
	protected void setNumStepsStillExecuting(int num) {
		fNumStepsStillExecuting = num;
	}

	/**
	 * Returns the queue of executeWithTargetAvailable() operations that still need to be processed
	 * @since 4.0
	 */
	protected LinkedList<TargetAvailableOperationInfo> getOperationsPending() {
		return fOperationsPending;
	}

	/**
	 * This method takes care of executing a batch of steps that were passed to
	 * ExecuteWithTargetAvailable().  The method is used to track the progress
	 * of all these batches of steps, so that we know exactly when all of them
	 * have been completed and the global sequence can be completed.
	 * @since 4.0
	 */
	protected void executeSteps(final TargetAvailableOperationInfo info) {
		fNumStepsStillExecuting++;

		// This RM propagates any error to the original rm of the actual steps.
		// Even in case of errors for these steps, we want to continue the overall sequence
		RequestMonitor stepsRm = new ImmediateRequestMonitor() {
			@Override
			protected void handleCompleted() {
				info.rm.setStatus(getStatus());
				// It is important to call rm.done() right away.
				// This is because some other operation we are performing might be waiting
				// for this one to be done.  If we try to wait for the entire sequence to be
				// done, then we will never finish because one monitor will never show as
				// done, waiting for the second one.
				info.rm.done();

				fExecuteQueuedOpsStepMonitor.requestMonitorDone(this);
				fNumStepsStillExecuting--;
				if (fNumStepsStillExecuting == 0) {
					fExecuteQueuedOpsStepMonitor.doneAdding();
				}
			}
		};

		fExecuteQueuedOpsStepMonitor.add(stepsRm);

		getExecutor().execute(new Sequence(getExecutor(), stepsRm) {
			@Override
			public Step[] getSteps() {
				return info.steps;
			}
		});
	}

	/**
	 * @since 3.0
	 */
	@Override
	public void executeWithTargetAvailable(IDMContext ctx, final Sequence.Step[] steps, final RequestMonitor rm) {
		if (!fOngoingOperation) {
			// We are the first operation of this kind currently requested
			// so we need to start the sequence
			fOngoingOperation = true;

			// We always go through our queue, even if we only have a single call to this method
			fOperationsPending.add(new TargetAvailableOperationInfo(ctx, steps, rm));

			// Steps that need to be executed to perform the operation
			final Step[] sequenceSteps = new Step[] { new IsTargetAvailableStep(ctx), new MakeTargetAvailableStep(),
					new ExecuteQueuedOperationsStep(), new RestoreTargetStateStep(), };

			// Once all the sequence is completed, we need to see if we have received
			// another request that we now need to process
			RequestMonitor sequenceCompletedRm = new RequestMonitor(getExecutor(), null) {
				@Override
				protected void handleSuccess() {
					fOngoingOperation = false;

					if (!fOperationsPending.isEmpty()) {
						// Darn, more operations came in.  Trigger their processing
						// by calling executeWithTargetAvailable() on the last one
						TargetAvailableOperationInfo info = fOperationsPending.removeLast();
						executeWithTargetAvailable(info.ctx, info.steps, info.rm);
					}
					// no other rm.done() needs to be called, they have all been handled already
				}

				@Override
				protected void handleFailure() {
					// If the sequence failed, we have to give up on the operation(s).
					// If we don't, we risk an infinite loop where we try, over and over
					// to perform an operation that keeps on failing.
					fOngoingOperation = false;

					// Complete each rm of the cancelled operations
					while (!fOperationsPending.isEmpty()) {
						RequestMonitor rm = fOperationsPending.poll().rm;
						rm.setStatus(getStatus());
						rm.done();
					}
					super.handleFailure();
				}
			};

			getExecutor().execute(new Sequence(getExecutor(), sequenceCompletedRm) {
				@Override
				public Step[] getSteps() {
					return sequenceSteps;
				}
			});
		} else {
			// We are currently already executing such an operation
			// If we are still in the process of executing steps, let's include this new set of steps.
			// This is important because some steps may depend on these new ones.
			if (fCurrentlyExecutingSteps) {
				executeSteps(new TargetAvailableOperationInfo(ctx, steps, rm));
			} else {
				// Too late to execute the new steps, so queue them for later
				fOperationsPending.add(new TargetAvailableOperationInfo(ctx, steps, rm));
			}
		}
	}

	/**
	 * This part of the sequence looks for all threads that will need to be suspended.
	 * @since 3.0
	 */
	protected class IsTargetAvailableStep extends Sequence.Step {
		final IDMContext fCtx;

		public IsTargetAvailableStep(IDMContext ctx) {
			fCtx = ctx;
		}

		private void getThreadToSuspend(IContainerDMContext containerDmc, final RequestMonitor rm) {
			// If the process is running, get its first thread which we will need to suspend
			fProcessService.getProcessesBeingDebugged(containerDmc, new ImmediateDataRequestMonitor<IDMContext[]>(rm) {
				@Override
				protected void handleSuccess() {
					IDMContext[] threads = getData();
					if (threads != null && threads.length > 0) {
						// Choose the first thread as the one to suspend
						fExecutionDmcToSuspendSet.add((IMIExecutionDMContext) threads[0]);
					}
					rm.done();
				}
			});
		}

		@Override
		public void execute(final RequestMonitor rm) {
			// Clear any old data before we start
			fExecutionDmcToSuspendSet.clear();

			// Get all processes being debugged to see which one are running
			// and need to be interrupted
			fProcessService.getProcessesBeingDebugged(fConnection.getContext(),
					new ImmediateDataRequestMonitor<IDMContext[]>(rm) {
						@Override
						protected void handleSuccess() {
							assert getData() != null;

							if (getData().length == 0) {
								// Happens at startup, starting with GDB 7.0.
								// This means the target is available.  Nothing to do.
								rm.done();
							} else {
								// Go through every process to see if it is running.
								// If it is running, get its first thread so we can interrupt it.
								CountingRequestMonitor crm = new ImmediateCountingRequestMonitor(rm);

								int numThreadsToSuspend = 0;
								for (IDMContext dmc : getData()) {
									IContainerDMContext containerDmc = (IContainerDMContext) dmc;
									if (!isSuspended(containerDmc)) {
										numThreadsToSuspend++;
										getThreadToSuspend(containerDmc, crm);
									}
								}
								crm.setDoneCount(numThreadsToSuspend);
							}
						}
					});
		}
	}

	/**
	 * Suspended all the threads we have selected.
	 * @since 3.0
	 */
	protected class MakeTargetAvailableStep extends Sequence.Step {

		/* public constructor required, so upper classes can override executeWithTargetAvailable */
		/** @since 4.5 */
		public MakeTargetAvailableStep() {
		}

		@Override
		public void execute(final RequestMonitor rm) {
			// Interrupt every first thread of the running processes
			CountingRequestMonitor crm = new ImmediateCountingRequestMonitor(rm);
			crm.setDoneCount(fExecutionDmcToSuspendSet.size());

			for (final IMIExecutionDMContext thread : fExecutionDmcToSuspendSet) {
				assert !fDisableNextRunningEventDmcSet.contains(thread);
				assert !fDisableNextSignalEventDmcSet.contains(thread);

				// Don't broadcast the next stopped signal event
				fDisableNextSignalEventDmcSet.add(thread);

				suspend(thread, new ImmediateRequestMonitor(crm) {
					@Override
					protected void handleFailure() {
						// We weren't able to suspend, so abort the operation
						fDisableNextSignalEventDmcSet.remove(thread);
						super.handleFailure();
					}
				});
			}
		}

		@Override
		public void rollBack(RequestMonitor rm) {
			Sequence.Step restoreStep = new RestoreTargetStateStep();
			restoreStep.execute(rm);
		}
	}

	/**
	 * This step of the sequence takes care of executing all the steps that
	 * were passed to ExecuteWithTargetAvailable().
	 * @since 4.0
	 */
	protected class ExecuteQueuedOperationsStep extends Sequence.Step {

		/* public constructor required, so upper classes can override executeWithTargetAvailable */
		/** @since 4.5 */
		public ExecuteQueuedOperationsStep() {
		}

		@Override
		public void execute(final RequestMonitor rm) {
			fCurrentlyExecutingSteps = true;

			// It is important to use an ImmediateExecutor for this RM, to make sure we don't risk getting a new
			// call to ExecuteWithTargetAvailable() when we just finished executing the steps.
			fExecuteQueuedOpsStepMonitor = new MultiRequestMonitor<RequestMonitor>(ImmediateExecutor.getInstance(),
					rm) {
				@Override
				protected void handleCompleted() {
					assert fOperationsPending.size() == 0;

					// We don't handle errors here.  Instead, we have already propagated any
					// errors to each rm for each set of steps

					fCurrentlyExecutingSteps = false;
					// Continue the sequence
					rm.done();
				}
			};
			// Tell the RM that we need to confirm when we are done adding sub-rms
			fExecuteQueuedOpsStepMonitor.requireDoneAdding();

			// All pending operations are independent of each other so we can
			// run them concurrently.
			while (!fOperationsPending.isEmpty()) {
				executeSteps(fOperationsPending.poll());
			}
		}
	}

	/**
	 * If the sequence had to interrupt the execution context of interest,
	 * this step will resume it again to reach the same state as when we started.
	 * @since 3.0
	 */
	protected class RestoreTargetStateStep extends Sequence.Step {

		/* public constructor required, so upper classes can override executeWithTargetAvailable */
		/** @since 4.5 */
		public RestoreTargetStateStep() {
		}

		@Override
		public void execute(final RequestMonitor rm) {
			// Resume every thread we had interrupted
			CountingRequestMonitor crm = new ImmediateCountingRequestMonitor(rm);
			crm.setDoneCount(fExecutionDmcToSuspendSet.size());

			for (final IMIExecutionDMContext thread : fExecutionDmcToSuspendSet) {

				assert !fDisableNextRunningEventDmcSet.contains(thread);
				fDisableNextRunningEventDmcSet.add(thread);

				// Can't use the resume() call because we 'silently' stopped
				// so resume() will not know we are actually stopped
				fConnection.queueCommand(fCommandFactory.createMIExecContinue(thread),
						new ImmediateDataRequestMonitor<MIInfo>(crm) {
							@Override
							protected void handleSuccess() {
								fSilencedSignalEventMap.remove(thread);
								super.handleSuccess();
							}

							@Override
							protected void handleFailure() {
								// Darn, we're unable to restart the target.  Must cleanup!
								fDisableNextRunningEventDmcSet.remove(thread);

								// We must also sent the Stopped event that we had kept silent
								MIStoppedEvent event = fSilencedSignalEventMap.remove(thread);
								if (event != null) {
									eventDispatched(event);
								} else {
									// Maybe the stopped event didn't arrive yet.
									// We don't want to silence it anymore
									fDisableNextSignalEventDmcSet.remove(thread);
								}

								super.handleFailure();
							}
						});
			}
		}
	}

	/* ******************************************************************************
	 * End of section to support operations even when the target is unavailable.
	 * ******************************************************************************/

	///////////////////////////////////////////////////////////////////////////
	// Event handlers
	///////////////////////////////////////////////////////////////////////////

	/**
	 * @nooverride This method is not intended to be re-implemented or extended by clients.
	 * @noreference This method is not intended to be referenced by clients.
	 */
	@DsfServiceEventHandler
	public void eventDispatched(final MIRunningEvent e) {
		if (fDisableNextRunningEventDmcSet.remove(e.getDMContext())) {
			// Don't broadcast the running event
			return;
		}

		MIThreadRunState threadState = fThreadRunStates.get(e.getDMContext());
		if (threadState != null && threadState.fLatestEvent instanceof IResumedDMEvent) {
			// Ignore multiple running events in a row.  They will only slow down the UI
			// for no added value.
			return;
		}

		if (fRunToLineActiveOperation == null && fStepInToSelectionActiveOperation == null) {
			// No special case here, i.e. send notification
			getSession().dispatchEvent(new ResumedEvent(e.getDMContext(), e), getProperties());
		} else {
			// Either RunToLine or StepIntoSelection operations are active
			if (threadState == null || threadState.fLatestEvent instanceof ISuspendedDMEvent) {
				// Need to send out Running event notification, only once per operation, then a stop event is expected
				// at the end of the operation
				getSession().dispatchEvent(new ResumedEvent(e.getDMContext(), e), getProperties());
			}
		}
	}

	/**
	 * @nooverride This method is not intended to be re-implemented or extended by clients.
	 * @noreference This method is not intended to be referenced by clients.
	 */
	@DsfServiceEventHandler
	public void eventDispatched(final MIStoppedEvent e) {
		// A disabled signal event is due to interrupting the target
		// to set a breakpoint.  This can happen during a run-to-line
		// or step-into operation, so we need to check it first.
		IMIExecutionDMContext threadDmc = DMContexts.getAncestorOfType(e.getDMContext(), IMIExecutionDMContext.class);
		if (e instanceof MISignalEvent && fDisableNextSignalEventDmcSet.remove(threadDmc)) {
			fSilencedSignalEventMap.put(threadDmc, e);
			// Don't broadcast the stopped event
			return;
		}

		if (processRunToLineStoppedEvent(e)) {
			// If RunToLine is not completed
			return;
		}

		if (!processStepIntoSelection(e)) {
			//Step into Selection is not in progress
			broadcastStop(e);
		}
	}

	private void broadcastStop(final MIStoppedEvent e) {
		IDMEvent<?> event = null;
		MIBreakpointDMContext bp = null;
		if (e instanceof MIBreakpointHitEvent) {
			String bpId = ((MIBreakpointHitEvent) e).getNumber();
			IBreakpointsTargetDMContext bpsTarget = DMContexts.getAncestorOfType(e.getDMContext(),
					IBreakpointsTargetDMContext.class);
			if (bpsTarget != null && !bpId.isEmpty()) {
				bp = new MIBreakpointDMContext(getSession().getId(), new IDMContext[] { bpsTarget }, bpId);
				event = new BreakpointHitEvent(e.getDMContext(), (MIBreakpointHitEvent) e, bp);
			}
		}
		if (event == null) {
			event = new SuspendedEvent(e.getDMContext(), e);
		}

		getSession().dispatchEvent(event, getProperties());
	}

	private boolean processStepIntoSelection(final MIStoppedEvent e) {
		if (fStepInToSelectionActiveOperation == null) {
			return false;
		}

		// First check if it is the right thread that stopped
		final IMIExecutionDMContext threadDmc = DMContexts.getAncestorOfType(e.getDMContext(),
				IMIExecutionDMContext.class);
		if (fStepInToSelectionActiveOperation.getThreadContext().equals(threadDmc)) {
			final MIFrame frame = e.getFrame();

			assert (fRunToLineActiveOperation == null);

			if (fStepInToSelectionActiveOperation.getRunToLineFrame() == null) {
				assert (fStepInToSelectionActiveOperation.getLine() == frame.getLine());
				// Shall now be at the runToline location
				fStepInToSelectionActiveOperation.setRunToLineFrame(frame);
			}

			// Step - Not at the right place just yet
			// Initiate an async call chain parent
			getStackDepth(threadDmc, new DataRequestMonitor<Integer>(getExecutor(), null) {
				private int originalStackDepth = fStepInToSelectionActiveOperation.getOriginalStackDepth();

				@Override
				protected void handleSuccess() {
					int frameDepth = getStackDepth();

					if (frameDepth > originalStackDepth) {
						//shall be true as this is using stepinto step type vs instruction stepinto
						assert (frameDepth == originalStackDepth + 1);

						// Check for a match
						if (StepIntoSelectionUtils.sameSignature(frame, fStepInToSelectionActiveOperation)) {
							// Hit !!
							stopStepIntoSelection(e);
							return;
						}

						// Located deeper in the stack, Shall continue step / search
						// Step return
						continueStepping(e, StepType.STEP_RETURN);
					} else if (frameDepth == originalStackDepth) {
						// Continue step / search as long as
						// this is the starting base line for the search
						String currentLocation = frame.getFile() + ":" + frame.getLine(); //$NON-NLS-1$
						String searchLineLocation = fStepInToSelectionActiveOperation.getFileLocation();
						if (currentLocation.equals(searchLineLocation)) {
							continueStepping(e, StepType.STEP_INTO);
						} else {
							// We have moved to a line
							// different from the base
							// search line i.e. missed the
							// target function !!
							StepIntoSelectionUtils.missedSelectedTarget(fStepInToSelectionActiveOperation);
							stopStepIntoSelection(e);
						}
					} else {
						// missed the target point
						StepIntoSelectionUtils.missedSelectedTarget(fStepInToSelectionActiveOperation);
					}
				}

				@Override
				protected void handleFailure() {
					// log error
					if (getStatus() != null) {
						GdbPlugin.getDefault().getLog().log(getStatus());
					}

					stopStepIntoSelection(e);
				}

				private int getStackDepth() {
					Integer stackDepth = null;
					if (isSuccess() && getData() != null) {
						stackDepth = getData();
						// This is the base frame, the original stack depth shall be updated
						if (frame == fStepInToSelectionActiveOperation.getRunToLineFrame()) {
							fStepInToSelectionActiveOperation.setOriginalStackDepth(stackDepth);
							originalStackDepth = stackDepth;
						}
					}

					if (stackDepth == null) {
						// Unsuccessful resolution of stack depth, default to same stack depth to detect a change of line within the original frame
						return fStepInToSelectionActiveOperation.getOriginalStackDepth();
					}

					return stackDepth.intValue();
				}
			});

			//Processing step into selection
			return true;
		}

		//The thread related to this event is outside the scope of the step into selection context
		return false;
	}

	private void stopStepIntoSelection(final MIStoppedEvent e) {
		fStepInToSelectionActiveOperation = null;
		// Need to broadcast the stop
		broadcastStop(e);
	}

	private void continueStepping(final MIStoppedEvent event, StepType steptype) {
		step(fStepInToSelectionActiveOperation.getThreadContext(), steptype, false,
				new RequestMonitor(getExecutor(), null) {
					@Override
					protected void handleFailure() {
						// log error
						if (getStatus() != null) {
							GdbPlugin.getDefault().getLog().log(getStatus());
						}

						stopStepIntoSelection(event);
					}
				});
	}

	private boolean processRunToLineStoppedEvent(final MIStoppedEvent e) {
		if (fRunToLineActiveOperation == null) {
			return false;
		}

		// First check if it is the right thread that stopped
		IMIExecutionDMContext threadDmc = DMContexts.getAncestorOfType(e.getDMContext(), IMIExecutionDMContext.class);
		if (fRunToLineActiveOperation.getThreadContext().equals(threadDmc)) {
			String bpId = ""; //$NON-NLS-1$
			if (e instanceof MIBreakpointHitEvent) {
				bpId = ((MIBreakpointHitEvent) e).getNumber();
			}

			String fileLocation = e.getFrame().getFile() + ":" + e.getFrame().getLine(); //$NON-NLS-1$
			String addrLocation = e.getFrame().getAddress();

			// Here we check three different things to see if we are stopped at the right place
			// 1- The actual location in the file.  But this does not work for breakpoints that
			//    were set on non-executable lines
			// 2- The address where the breakpoint was set.  But this does not work for breakpoints
			//    that have multiple addresses (GDB returns <MULTIPLE>.)  I think that is for multi-process
			// 3- The breakpoint id that was hit.  But this does not work if another breakpoint
			//    was also set on the same line because GDB may return that breakpoint as being hit.
			//
			// So this works for the large majority of cases.  The case that won't work is when the user
			// does a runToLine to a line that is non-executable AND has another breakpoint AND
			// has multiple addresses for the breakpoint.  I'm mean, come on!
			if (fileLocation.equals(fRunToLineActiveOperation.getFileLocation())
					|| addrLocation.equals(fRunToLineActiveOperation.getAddrLocation())
					|| bpId.equals(fRunToLineActiveOperation.getBreakpointId())) {
				// We stopped at the right place. All is well.
				// Run to line completed
				fRunToLineActiveOperation = null;
			} else {
				// The right thread stopped but not at the right place yet
				if (fRunToLineActiveOperation.shouldSkipBreakpoints() && e instanceof MIBreakpointHitEvent) {
					fConnection.queueCommand(
							fCommandFactory.createMIExecContinue(fRunToLineActiveOperation.getThreadContext()),
							new DataRequestMonitor<MIInfo>(getExecutor(), null));

					// Continue i.e. Don't send the stop event since we are
					// resuming again.
					return true;
				} else {
					// Stopped for any other reasons. Just remove our temporary one
					// since we don't want it to hit later
					//
					// Note that in Non-stop, we don't cancel a run-to-line when a new
					// breakpoint is inserted. This is because the new breakpoint could
					// be for another thread altogether and should not affect the current thread.
					IBreakpointsTargetDMContext bpDmc = DMContexts.getAncestorOfType(
							fRunToLineActiveOperation.getThreadContext(), IBreakpointsTargetDMContext.class);

					fConnection.queueCommand(
							fCommandFactory.createMIBreakDelete(bpDmc,
									new String[] { fRunToLineActiveOperation.getBreakpointId() }),
							new DataRequestMonitor<MIInfo>(getExecutor(), null));
					fRunToLineActiveOperation = null;
					fStepInToSelectionActiveOperation = null;
				}
			}
		}

		return false;
	}

	/**
	 * @nooverride This method is not intended to be re-implemented or extended by clients.
	 * @noreference This method is not intended to be referenced by clients.
	 */
	@DsfServiceEventHandler
	public void eventDispatched(final MIThreadCreatedEvent e) {
		IContainerDMContext containerDmc = e.getDMContext();
		IMIExecutionDMContext executionCtx = null;
		if (e.getStrId() != null) {
			executionCtx = createMIExecutionContext(containerDmc, e.getStrId());
		}
		getSession().dispatchEvent(new StartedDMEvent(executionCtx, e), getProperties());
	}

	/**
	 * @nooverride This method is not intended to be re-implemented or extended by clients.
	 * @noreference This method is not intended to be referenced by clients.
	 */
	@DsfServiceEventHandler
	public void eventDispatched(final MIThreadExitEvent e) {
		IContainerDMContext containerDmc = e.getDMContext();
		IMIExecutionDMContext executionCtx = null;
		if (e.getStrId() != null) {
			executionCtx = createMIExecutionContext(containerDmc, e.getStrId());
		}
		getSession().dispatchEvent(new ExitedDMEvent(executionCtx, e), getProperties());
	}

	/**
	 * @nooverride This method is not intended to be re-implemented or extended by clients.
	 * @noreference This method is not intended to be referenced by clients.
	 */
	@DsfServiceEventHandler
	public void eventDispatched(ResumedEvent e) {
		IExecutionDMContext ctx = e.getDMContext();
		if (ctx instanceof IMIExecutionDMContext) {
			updateThreadState((IMIExecutionDMContext) ctx, e);
		}
	}

	/**
	 * @nooverride This method is not intended to be re-implemented or extended by clients.
	 * @noreference This method is not intended to be referenced by clients.
	 */
	@DsfServiceEventHandler
	public void eventDispatched(SuspendedEvent e) {
		IExecutionDMContext ctx = e.getDMContext();
		if (ctx instanceof IMIExecutionDMContext) {
			updateThreadState((IMIExecutionDMContext) ctx, e);
		}
	}

	/**
	 * @nooverride This method is not intended to be re-implemented or extended by clients.
	 * @noreference This method is not intended to be referenced by clients.
	 */
	@DsfServiceEventHandler
	public void eventDispatched(StartedDMEvent e) {
		IExecutionDMContext executionCtx = e.getDMContext();
		if (executionCtx instanceof IMIExecutionDMContext) {
			if (fThreadRunStates.get(executionCtx) == null) {
				fThreadRunStates.put((IMIExecutionDMContext) executionCtx, new MIThreadRunState());
			}
		}
	}

	/**
	 * @nooverride This method is not intended to be re-implemented or extended by clients.
	 * @noreference This method is not intended to be referenced by clients.
	 */
	@DsfServiceEventHandler
	public void eventDispatched(ExitedDMEvent e) {
		fThreadRunStates.remove(e.getDMContext());
	}

	/**
	 * @nooverride This method is not intended to be re-implemented or extended by clients.
	 * @noreference This method is not intended to be referenced by clients.
	 */
	@DsfServiceEventHandler
	public void eventDispatched(ICommandControlShutdownDMEvent e) {
		fTerminated = true;
	}

	/**
	 * @nooverride This method is not intended to be re-implemented or extended by clients.
	 * @noreference This method is not intended to be referenced by clients.
	 *
	 * @since 2.0
	 */
	@DsfServiceEventHandler
	public void eventDispatched(MIInferiorExitEvent e) {
		if (fRunToLineActiveOperation != null) {
			IBreakpointsTargetDMContext bpDmc = DMContexts
					.getAncestorOfType(fRunToLineActiveOperation.getThreadContext(), IBreakpointsTargetDMContext.class);
			String bpId = fRunToLineActiveOperation.getBreakpointId();

			fConnection.queueCommand(fCommandFactory.createMIBreakDelete(bpDmc, new String[] { bpId }),
					new DataRequestMonitor<MIInfo>(getExecutor(), null));
			fRunToLineActiveOperation = null;
		}
		fStepInToSelectionActiveOperation = null;
	}

	@Override
	public void flushCache(IDMContext context) {
		refreshThreadStates();
	}

	/**
	 * Gets the state of each thread from GDB and updates our internal map.
	 * @since 4.1
	 */
	protected void refreshThreadStates() {
		fConnection.queueCommand(fCommandFactory.createMIThreadInfo(fConnection.getContext()),
				new DataRequestMonitor<MIThreadInfoInfo>(getExecutor(), null) {
					@Override
					protected void handleSuccess() {
						MIThread[] threadList = getData().getThreadList();
						for (MIThread thread : threadList) {
							String threadId = thread.getThreadId();
							IMIContainerDMContext containerDmc = fProcessService
									.createContainerContextFromThreadId(fConnection.getContext(), threadId);
							IProcessDMContext processDmc = DMContexts.getAncestorOfType(containerDmc,
									IProcessDMContext.class);
							IThreadDMContext threadDmc = fProcessService.createThreadContext(processDmc, threadId);
							IMIExecutionDMContext execDmc = fProcessService.createExecutionContext(containerDmc,
									threadDmc, threadId);

							MIThreadRunState threadState = fThreadRunStates.get(execDmc);
							if (threadState != null) {
								// We may not know this thread.  This can happen when dealing with a remote
								// where thread events are not reported immediately.
								// However, the -thread-info command we just sent will make
								// GDB send those events.  Therefore, we can just ignore threads we don't
								// know about, and wait for those events.
								if (MIThread.MI_THREAD_STATE_RUNNING.equals(thread.getState())) {
									if (threadState.fSuspended == true) {
										// We missed a resumed event!  Send it now.
										IResumedDMEvent resumedEvent = new ResumedEvent(execDmc, null);
										fConnection.getSession().dispatchEvent(resumedEvent, getProperties());
									}
								} else if (MIThread.MI_THREAD_STATE_STOPPED.equals(thread.getState())) {
									if (threadState.fSuspended == false) {
										// We missed a suspend event!  Send it now.
										ISuspendedDMEvent suspendedEvent = new SuspendedEvent(execDmc, null);
										fConnection.getSession().dispatchEvent(suspendedEvent, getProperties());
									}
								} else {
									assert false : "Invalid thread state: " + thread.getState(); //$NON-NLS-1$
								}
							}
						}
					}
				});
	}

	private void moveToLocation(final IExecutionDMContext context, final String location,
			final Map<String, Object> bpAttributes, final RequestMonitor rm) {

		// first create a temporary breakpoint to stop the execution at
		// the location we are about to jump to
		IBreakpoints bpService = getServicesTracker().getService(IBreakpoints.class);
		IBreakpointsTargetDMContext bpDmc = DMContexts.getAncestorOfType(context, IBreakpointsTargetDMContext.class);
		if (bpService != null && bpDmc != null) {
			bpService.insertBreakpoint(bpDmc, bpAttributes,
					new DataRequestMonitor<IBreakpointDMContext>(getExecutor(), rm) {
						@Override
						protected void handleSuccess() {
							// Now resume at the proper location
							resumeAtLocation(context, location, rm);
						}
					});
		} else {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED,
					"Unable to set breakpoint", null)); //$NON-NLS-1$
			rm.done();
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.dsf.debug.service.IRunControl2#canRunToLine(org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext, java.lang.String, int, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
	 */
	/**
	 * @since 3.0
	 */
	@Override
	public void canRunToLine(IExecutionDMContext context, String sourceFile, int lineNumber,
			DataRequestMonitor<Boolean> rm) {
		canResume(context, rm);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.dsf.debug.service.IRunControl2#runToLine(org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext, java.lang.String, int, boolean, org.eclipse.cdt.dsf.concurrent.RequestMonitor)
	 */
	/**
	 * @since 3.0
	 */
	@Override
	public void runToLine(final IExecutionDMContext context, String sourceFile, final int lineNumber,
			final boolean skipBreakpoints, final RequestMonitor rm) {

		determineDebuggerPath(context, sourceFile, new ImmediateDataRequestMonitor<String>(rm) {
			@Override
			protected void handleSuccess() {
				runToLocation(context, getData() + ":" + Integer.toString(lineNumber), skipBreakpoints, rm); //$NON-NLS-1$
			}
		});
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.dsf.debug.service.IRunControl2#canRunToAddress(org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext, org.eclipse.cdt.core.IAddress, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
	 */
	/**
	 * @since 3.0
	 */
	@Override
	public void canRunToAddress(IExecutionDMContext context, IAddress address, DataRequestMonitor<Boolean> rm) {
		canResume(context, rm);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.dsf.debug.service.IRunControl2#runToAddress(org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext, org.eclipse.cdt.core.IAddress, boolean, org.eclipse.cdt.dsf.concurrent.RequestMonitor)
	 */
	/**
	 * @since 3.0
	 */
	@Override
	public void runToAddress(IExecutionDMContext context, IAddress address, boolean skipBreakpoints,
			RequestMonitor rm) {
		runToLocation(context, "*0x" + address.toString(16), skipBreakpoints, rm); //$NON-NLS-1$
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.dsf.debug.service.IRunControl2#canMoveToLine(org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext, java.lang.String, int, boolean, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
	 */
	/**
	 * @since 3.0
	 */
	@Override
	public void canMoveToLine(IExecutionDMContext context, String sourceFile, int lineNumber, boolean resume,
			DataRequestMonitor<Boolean> rm) {
		canResume(context, rm);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.dsf.debug.service.IRunControl2#moveToLine(org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext, java.lang.String, int, boolean, org.eclipse.cdt.dsf.concurrent.RequestMonitor)
	 */
	/**
	 * @since 3.0
	 */
	@Override
	public void moveToLine(final IExecutionDMContext context, String sourceFile, final int lineNumber,
			final boolean resume, final RequestMonitor rm) {
		final IMIExecutionDMContext threadExecDmc = DMContexts.getAncestorOfType(context, IMIExecutionDMContext.class);
		if (threadExecDmc == null) {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.REQUEST_FAILED,
					"Invalid thread context", null)); //$NON-NLS-1$
			rm.done();
		} else {
			determineDebuggerPath(context, sourceFile, new ImmediateDataRequestMonitor<String>(rm) {
				@Override
				protected void handleSuccess() {
					String debuggerPath = getData();

					String location = debuggerPath + ":" + lineNumber; //$NON-NLS-1$
					if (resume) {
						resumeAtLocation(context, location, rm);
					} else {
						// Create the breakpoint attributes
						Map<String, Object> attr = new HashMap<>();
						attr.put(MIBreakpoints.BREAKPOINT_TYPE, MIBreakpoints.BREAKPOINT);
						attr.put(MIBreakpoints.FILE_NAME, debuggerPath);
						attr.put(MIBreakpoints.LINE_NUMBER, lineNumber);
						attr.put(MIBreakpointDMData.IS_TEMPORARY, true);
						attr.put(MIBreakpointDMData.THREAD_ID, threadExecDmc.getThreadId());

						// Now do the operation
						moveToLocation(context, location, attr, rm);
					}
				}
			});
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.dsf.debug.service.IRunControl2#canMoveToAddress(org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext, org.eclipse.cdt.core.IAddress, boolean, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
	 */
	/**
	 * @since 3.0
	 */
	@Override
	public void canMoveToAddress(IExecutionDMContext context, IAddress address, boolean resume,
			DataRequestMonitor<Boolean> rm) {
		canResume(context, rm);
	}

	/** (non-Javadoc)
	 * @see org.eclipse.cdt.dsf.debug.service.IRunControl2#moveToAddress(org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext, org.eclipse.cdt.core.IAddress, boolean, org.eclipse.cdt.dsf.concurrent.RequestMonitor)
	 * @since 3.0
	 */
	@Override
	public void moveToAddress(IExecutionDMContext context, IAddress address, boolean resume, RequestMonitor rm) {
		IMIExecutionDMContext threadExecDmc = DMContexts.getAncestorOfType(context, IMIExecutionDMContext.class);
		if (threadExecDmc == null) {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.REQUEST_FAILED,
					"Invalid thread context", null)); //$NON-NLS-1$
			rm.done();
		} else {
			String location = "*0x" + address.toString(16); //$NON-NLS-1$
			if (resume)
				resumeAtLocation(context, location, rm);
			else {
				// Create the breakpoint attributes
				Map<String, Object> attr = new HashMap<>();
				attr.put(MIBreakpoints.BREAKPOINT_TYPE, MIBreakpoints.BREAKPOINT);
				attr.put(MIBreakpoints.ADDRESS, "0x" + address.toString(16)); //$NON-NLS-1$
				attr.put(MIBreakpointDMData.IS_TEMPORARY, true);
				attr.put(MIBreakpointDMData.THREAD_ID, threadExecDmc.getThreadId());

				// Now do the operation
				moveToLocation(context, location, attr, rm);
			}
		}
	}

	/** @since 4.0 */
	@Override
	public IRunMode getRunMode() {
		return MIRunMode.NON_STOP;
	}

	/** @since 4.0 */
	@Override
	public boolean isTargetAcceptingCommands() {
		// Always accepting commands in non-stop mode
		return true;
	}

	/**
	 * Determine the path that should be sent to the debugger as per the source lookup service.
	 *
	 * @param dmc A context that can be used to obtain the sourcelookup context.
	 * @param hostPath The path of the file on the host, which must be converted.
	 * @param rm The result of the conversion.
	 */
	private void determineDebuggerPath(IDMContext dmc, String hostPath, final DataRequestMonitor<String> rm) {
		ISourceLookup sourceLookup = getServicesTracker().getService(ISourceLookup.class);
		ISourceLookupDMContext srcDmc = DMContexts.getAncestorOfType(dmc, ISourceLookupDMContext.class);
		if (sourceLookup == null || srcDmc == null) {
			// Source lookup not available for given context, use the host
			// path for the debugger path.
			// Hack around a MinGW bug; see 369622 (and also 196154 and 232415)
			rm.done(adjustDebuggerPath(hostPath));
			return;
		}

		sourceLookup.getDebuggerPath(srcDmc, hostPath, new DataRequestMonitor<String>(getExecutor(), rm) {
			@Override
			protected void handleSuccess() {
				// Hack around a MinGW bug; see 369622 (and also 196154 and 232415)
				rm.done(adjustDebuggerPath(getData()));
			}
		});
	}

	/**
	 * See bug 196154
	 *
	 * @param path
	 *            the absolute path to the source file
	 * @return the adjusted path provided by the breakpoints service
	 */
	private String adjustDebuggerPath(String path) {
		IBreakpoints breakpoints = getServicesTracker().getService(IBreakpoints.class);
		return (breakpoints instanceof IMIBreakpointPathAdjuster)
				? ((IMIBreakpointPathAdjuster) breakpoints).adjustDebuggerPath(path)
				: path;
	}

	///////////////////////////////////////////////////////////////////////////
	// IMultiRunControl implementation
	///////////////////////////////////////////////////////////////////////////

	// Although multi-process in only supported for GDB >= 7.2, it is simpler
	// to code for the multi-process case all the time, since it is a superset
	// of the single-process case.

	///////////////////////////////////////////////////////////////////////////
	// Multi-resume implementation:
	//
	// If one or more more threads of one or many processes are selected, we want to
	// resume each thread (once).
	//
	// If one or more more processes are selected, we want to resume each process (once).
	//
	// If a process is selected along with one or more threads of that same process,
	// what does the user want us to do?  Selecting the process will resume all its
	// threads, but what do we do with the selected threads?  Why are they
	// selected?  In an attempt to be user friendly, lets assume that the user
	// wants to resume the entire process, so we ignore the selected threads part of that
	// process since they will be resumed anyway.
	//
	// The same logic applies to multi-suspend.
	///////////////////////////////////////////////////////////////////////////

	/** @since 4.1 */
	@Override
	public void canResumeSome(IExecutionDMContext[] contexts, DataRequestMonitor<Boolean> rm) {
		assert contexts != null;

		if (fRunControlOperationsEnabled == false) {
			rm.done(false);
			return;
		}

		List<IExecutionDMContext> execDmcToResumeList = extractContextsForOperation(contexts);

		// If any of the threads or processes can be resumed, we allow
		// the user to perform the operation.
		for (IExecutionDMContext execDmc : execDmcToResumeList) {
			if (doCanResume(execDmc)) {
				rm.done(true);
				return;
			}
		}

		// Didn't find anything that could be resumed.
		rm.done(false);
	}

	/** @since 4.1 */
	@Override
	public void canResumeAll(IExecutionDMContext[] contexts, DataRequestMonitor<Boolean> rm) {
		assert contexts != null;

		if (fRunControlOperationsEnabled == false) {
			rm.done(false);
			return;
		}

		List<IExecutionDMContext> execDmcToResumeList = extractContextsForOperation(contexts);

		// If any of the threads or processes cannot be resumed, we don't allow
		// the user to perform the operation.
		for (IExecutionDMContext execDmc : execDmcToResumeList) {
			if (!doCanResume(execDmc)) {
				rm.done(false);
				return;
			}
		}

		// Everything can be resumed
		rm.done(true);
	}

	/**
	 * {@inheritDoc}
	 *
	 * For GDB, a separate resume command will be sent, one for each context
	 * that can be resumed.
	 * @since 4.1
	 */
	@Override
	public void resume(IExecutionDMContext[] contexts, RequestMonitor rm) {
		assert contexts != null;

		List<IExecutionDMContext> execDmcToResumeList = extractContextsForOperation(contexts);

		CountingRequestMonitor crm = new CountingRequestMonitor(getExecutor(), rm);
		int count = 0;

		// Perform resume operation on each thread or process that can be resumed
		for (IExecutionDMContext execDmc : execDmcToResumeList) {
			if (doCanResume(execDmc)) {
				count++;
				resume(execDmc, crm);
			}
		}

		crm.setDoneCount(count);
	}

	///////////////////////////////////////////////////////////////////////////
	// Multi-suspend implementation:
	//  see details of the multi-resume implementation above.
	///////////////////////////////////////////////////////////////////////////

	/** @since 4.1 */
	@Override
	public void canSuspendSome(IExecutionDMContext[] contexts, DataRequestMonitor<Boolean> rm) {
		assert contexts != null;

		if (fRunControlOperationsEnabled == false) {
			rm.done(false);
			return;
		}

		List<IExecutionDMContext> execDmcToSuspendList = extractContextsForOperation(contexts);

		// If any of the threads or processes can be suspended, we allow
		// the user to perform the operation.
		for (IExecutionDMContext execDmc : execDmcToSuspendList) {
			if (doCanSuspend(execDmc)) {
				rm.done(true);
				return;
			}
		}

		// Didn't find anything that could be suspended.
		rm.done(false);
	}

	/** @since 4.1 */
	@Override
	public void canSuspendAll(IExecutionDMContext[] contexts, DataRequestMonitor<Boolean> rm) {
		assert contexts != null;

		if (fRunControlOperationsEnabled == false) {
			rm.done(false);
			return;
		}

		List<IExecutionDMContext> execDmcToSuspendList = extractContextsForOperation(contexts);

		// If any of the threads or processes cannot be suspended, we don't allow
		// the user to perform the operation.
		for (IExecutionDMContext execDmc : execDmcToSuspendList) {
			if (!doCanSuspend(execDmc)) {
				rm.done(false);
				return;
			}
		}

		// Everything can be suspended
		rm.done(true);
	}

	/** @since 4.1 */
	@Override
	public void isSuspendedSome(IExecutionDMContext[] contexts, DataRequestMonitor<Boolean> rm) {
		assert contexts != null;

		List<IExecutionDMContext> execDmcSuspendedList = extractContextsForOperation(contexts);

		// Look for any thread or process that is suspended
		for (IExecutionDMContext execDmc : execDmcSuspendedList) {
			if (isSuspended(execDmc)) {
				rm.done(true);
				return;
			}
		}

		// Didn't find anything that was suspended.
		rm.done(false);
	}

	/** @since 4.1 */
	@Override
	public void isSuspendedAll(IExecutionDMContext[] contexts, DataRequestMonitor<Boolean> rm) {
		assert contexts != null;

		List<IExecutionDMContext> execDmcSuspendedList = extractContextsForOperation(contexts);

		// Look for any thread or process that is not suspended
		for (IExecutionDMContext execDmc : execDmcSuspendedList) {
			if (!isSuspended(execDmc)) {
				rm.done(false);
				return;
			}
		}

		// Everything is suspended.
		rm.done(true);
	}

	/**
	 * {@inheritDoc}
	 *
	 * For GDB, a separate suspend command will be sent, one for each context
	 * that can be suspended.
	 * @since 4.1
	 */
	@Override
	public void suspend(IExecutionDMContext[] contexts, RequestMonitor rm) {
		assert contexts != null;

		List<IExecutionDMContext> execDmcToSuspendList = extractContextsForOperation(contexts);

		CountingRequestMonitor crm = new CountingRequestMonitor(getExecutor(), rm);
		int count = 0;

		// Perform resume operation on each thread or process that can be resumed
		for (IExecutionDMContext execDmc : execDmcToSuspendList) {
			if (doCanSuspend(execDmc)) {
				count++;
				suspend(execDmc, crm);
			}
		}

		crm.setDoneCount(count);
	}

	///////////////////////////////////////////////////////////////////////////
	// Multi-step implementation.  Not implemented yet.  See bug 330974.
	///////////////////////////////////////////////////////////////////////////

	/** @since 4.1 */
	@Override
	public void canStepSome(IExecutionDMContext[] contexts, StepType stepType, DataRequestMonitor<Boolean> rm) {
		rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, "Not implemented.", //$NON-NLS-1$
				null));
	}

	/** @since 4.1 */
	@Override
	public void canStepAll(IExecutionDMContext[] contexts, StepType stepType, DataRequestMonitor<Boolean> rm) {
		rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, "Not implemented.", //$NON-NLS-1$
				null));
	}

	/** @since 4.1 */
	@Override
	public void isSteppingSome(IExecutionDMContext[] contexts, DataRequestMonitor<Boolean> rm) {
		rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, "Not implemented.", //$NON-NLS-1$
				null));
	}

	/** @since 4.1 */
	@Override
	public void isSteppingAll(IExecutionDMContext[] contexts, DataRequestMonitor<Boolean> rm) {
		rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, "Not implemented.", //$NON-NLS-1$
				null));
	}

	/** @since 4.1 */
	@Override
	public void step(IExecutionDMContext[] contexts, StepType stepType, RequestMonitor rm) {
		rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, "Not implemented.", //$NON-NLS-1$
				null));
	}

	/**
	 * Removes duplicates from the list of execution contexts, in case the same thread
	 * or process is present more than once.
	 *
	 * Also, remove any thread that is part of a process that is also present.  This is
	 * because an operation on the process will affect all its threads anyway.
	 */
	private List<IExecutionDMContext> extractContextsForOperation(IExecutionDMContext[] contexts) {
		// Remove duplicate contexts by using a set
		Set<IExecutionDMContext> specifiedExedDmcSet = new HashSet<>(Arrays.asList(contexts));

		// A list that ignores threads for which the process is also present
		List<IExecutionDMContext> execDmcForOperationList = new ArrayList<>(specifiedExedDmcSet.size());

		// Check for the case of a process selected along with some of its threads
		for (IExecutionDMContext execDmc : specifiedExedDmcSet) {
			if (execDmc instanceof IContainerDMContext) {
				// This is a process: it is automatically part of our list
				execDmcForOperationList.add(execDmc);
			} else {
				// Get the process for this thread
				IContainerDMContext containerDmc = DMContexts.getAncestorOfType(execDmc, IContainerDMContext.class);
				// Check if that process is also present
				if (specifiedExedDmcSet.contains(containerDmc) == false) {
					// This thread does not belong to a process that is selected, so we keep it.
					execDmcForOperationList.add(execDmc);
				}
			}
		}
		return execDmcForOperationList;
	}

	/**
	 * @since 4.2
	 */
	@Override
	public void canStepIntoSelection(IExecutionDMContext context, String sourceFile, int lineNumber,
			IFunctionDeclaration selectedFunction, DataRequestMonitor<Boolean> rm) {
		canStep(context, StepType.STEP_INTO, rm);
	}

	/**
	 * @since 4.2
	 */
	@Override
	public void stepIntoSelection(final IExecutionDMContext context, String sourceFile, final int lineNumber,
			final boolean skipBreakpoints, final IFunctionDeclaration selectedFunction, final RequestMonitor rm) {
		determineDebuggerPath(context, sourceFile, new ImmediateDataRequestMonitor<String>(rm) {
			@Override
			protected void handleSuccess() {
				stepIntoSelection(context, lineNumber, getData() + ":" + Integer.toString(lineNumber), skipBreakpoints, //$NON-NLS-1$
						selectedFunction, rm);
			}
		});
	}

	/**
	 * Help method used when the stopped event has not been broadcasted e.g. in the middle of step into selection
	 *
	 * @param dmc
	 * @param rm
	 */
	private void getStackDepth(final IMIExecutionDMContext dmc, final DataRequestMonitor<Integer> rm) {
		if (dmc != null) {
			fConnection.queueCommand(fCommandFactory.createMIStackInfoDepth(dmc),
					new DataRequestMonitor<MIStackInfoDepthInfo>(getExecutor(), rm) {
						@Override
						protected void handleSuccess() {
							rm.setData(getData().getDepth());
							rm.done();
						}
					});
		} else {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context", null)); //$NON-NLS-1$
			rm.done();
		}
	}
}

Back to the top