Skip to content

Commit 49233d0

Browse files
auto-submit[bot]auto-submit[bot]
andauthored
Reverts "Disable async mode with LLDB (flutter#184768)" (flutter#184868)
<!-- start_original_pr_link --> Reverts: flutter#184768 <!-- end_original_pr_link --> <!-- start_initiating_author --> Initiated by: flar <!-- end_initiating_author --> <!-- start_revert_reason --> Reason for reverting: flutter#184867 <!-- end_revert_reason --> <!-- start_original_pr_author --> Original PR Author: vashworth <!-- end_original_pr_author --> <!-- start_reviewers --> Reviewed By: {hellohuanlin} <!-- end_reviewers --> <!-- start_revert_body --> This change reverts the following previous change: @mraleph proposed a [better fix](flutter#184254 (comment)) for the LLDB Xcode 26.4 breakage and I confirmed that it works. This PR reverts the first fix and adopts the new one, which is to set LLDB to not use async mode. For flutter#184254. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [AI contribution guidelines] and understand my responsibilities, or I am not using AI tools. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. If this change needs to override an active code freeze, provide a comment explaining why. The code freeze workflow can be overridden by code reviewers. See pinned issues for any active code freezes with guidance. **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [AI contribution guidelines]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#ai-contribution-guidelines [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md <!-- end_revert_body --> Co-authored-by: auto-submit[bot] <flutter-engprod-team@google.com>
1 parent c6f719d commit 49233d0

5 files changed

Lines changed: 455 additions & 44 deletions

File tree

packages/flutter_tools/lib/src/ios/core_devices.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,13 @@ class IOSCoreDeviceLauncher {
3838
required XcodeDebug xcodeDebug,
3939
required FileSystem fileSystem,
4040
required ProcessUtils processUtils,
41+
required Xcode xcode,
4142
@visibleForTesting LLDB? lldb,
4243
}) : _coreDeviceControl = coreDeviceControl,
4344
_logger = logger,
4445
_xcodeDebug = xcodeDebug,
4546
_fileSystem = fileSystem,
46-
_lldb = lldb ?? LLDB(logger: logger, processUtils: processUtils);
47+
_lldb = lldb ?? LLDB(logger: logger, processUtils: processUtils, xcode: xcode);
4748

4849
final IOSCoreDeviceControl _coreDeviceControl;
4950
final Logger _logger;

packages/flutter_tools/lib/src/ios/lldb.dart

Lines changed: 58 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,22 @@ import '../base/io.dart';
1111
import '../base/logger.dart';
1212
import '../base/process.dart';
1313
import '../base/utils.dart';
14+
import '../base/version.dart';
15+
import '../macos/xcode.dart';
1416

1517
/// LLDB is the default debugger in Xcode on macOS. Once the application has
1618
/// launched on a physical iOS device, you can attach to it using LLDB.
1719
///
1820
/// See `xcrun devicectl device process launch --help` for more information.
1921
class LLDB {
20-
LLDB({required Logger logger, required ProcessUtils processUtils})
21-
: _logger = logger,
22+
LLDB({required Logger logger, required ProcessUtils processUtils, required Xcode xcode})
23+
: _xcode = xcode,
24+
_logger = logger,
2225
_processUtils = processUtils;
2326

2427
final Logger _logger;
2528
final ProcessUtils _processUtils;
29+
final Xcode _xcode;
2630

2731
_LLDBProcess? _lldbProcess;
2832

@@ -42,10 +46,10 @@ class LLDB {
4246
/// Example: (lldb) Process 6152 stopped
4347
static final _lldbProcessStopped = RegExp(r'Process \d* stopped');
4448

45-
/// Pattern of lldb log when the process is resuming and the breakpoint is added.
49+
/// Pattern of lldb log when the process is resuming.
4650
///
47-
/// Example: (lldb) 1 location added to breakpoint 1
48-
static final _lldbProcessResuming = RegExp(r'location added to breakpoint');
51+
/// Example: (lldb) Process 6152 resuming
52+
static final _lldbProcessResuming = RegExp(r'Process \d+ resuming');
4953

5054
/// Pattern of lldb log when the breakpoint is added.
5155
///
@@ -74,9 +78,6 @@ frame.GetThread().GetProcess().WriteMemory(base, data, error)
7478
if not error.Success():
7579
print(f'Failed to write into {base}[+{page_len}]', error)
7680
return
77-
78-
# If the returned value is False, that tells LLDB not to stop at the breakpoint
79-
return False
8081
''';
8182

8283
/// Starts an LLDB process and inputs commands to start debugging the [appProcessId].
@@ -147,19 +148,50 @@ return False
147148
logger: _logger,
148149
);
149150

150-
final StreamSubscription<String> stdoutSubscription = _lldbProcess!.stdout
151-
.transform(utf8LineDecoder)
152-
.listen((String line) {
153-
if (_isAttached && !_ignoreLog(line)) {
154-
// Only forwards logs after LLDB is attached. All logs before then are part of the
155-
// attach process.
151+
void printLine(String line) {
152+
if (_isAttached && !_ignoreLog(line)) {
153+
// Only forwards logs after LLDB is attached. All logs before then are part of the
154+
// attach process.
156155

157-
lldbLogForwarder.addLog(line);
158-
} else {
159-
_logger.printTrace('[lldb]: $line');
160-
_logCompleter?.checkForMatch(line);
161-
}
162-
});
156+
lldbLogForwarder.addLog(line);
157+
} else {
158+
_logger.printTrace('[lldb]: $line');
159+
_logCompleter?.checkForMatch(line);
160+
}
161+
}
162+
163+
String? processStopLine;
164+
var suppressLogs = false;
165+
166+
final StreamSubscription<String>
167+
stdoutSubscription = _lldbProcess!.stdout.transform(utf8LineDecoder).listen((String line) {
168+
// Skip all logs between process stop and process resume if the stop was caused by a breakpoint
169+
if (line.contains(_lldbProcessStopped)) {
170+
processStopLine = line;
171+
return;
172+
}
173+
// If the last log was a proces stop log, check if it was caused by a breakpoint.
174+
if (processStopLine != null) {
175+
if (line.contains('stop reason = breakpoint')) {
176+
// When we stop due to a breakpoint, supress all logs until we resume.
177+
_lldbProcess?.stdinWriteln('process continue');
178+
suppressLogs = true;
179+
} else {
180+
// Otherwise, print the process stop log.
181+
printLine(processStopLine!);
182+
}
183+
processStopLine = null;
184+
}
185+
if (suppressLogs) {
186+
if (line.contains(_lldbProcessResuming)) {
187+
// After resuming, stop supressing logs.
188+
suppressLogs = false;
189+
}
190+
return;
191+
}
192+
193+
printLine(line);
194+
});
163195

164196
final StreamSubscription<String> stderrSubscription = _lldbProcess!.stderr
165197
.transform(utf8LineDecoder)
@@ -225,9 +257,12 @@ return False
225257
_breakpointPattern,
226258
).then((value) => value, onError: _handleAsyncError);
227259

228-
await _lldbProcess?.stdinWriteln(
229-
r"breakpoint set --func-regex '^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$'",
230-
);
260+
final Version? xcodeVersion = _xcode.currentVersion;
261+
final bool useManualContinue = xcodeVersion != null && (xcodeVersion >= Version(26, 4, 0));
262+
final breakpointSetCommand = useManualContinue
263+
? r"breakpoint set --func-regex '^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$'"
264+
: r"breakpoint set --auto-continue true --func-regex '^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$'";
265+
await _lldbProcess?.stdinWriteln(breakpointSetCommand);
231266
final String log = await futureLog;
232267
final Match? match = _breakpointPattern.firstMatch(log);
233268
final String? breakpointId = match?.group(1);
@@ -240,11 +275,6 @@ return False
240275
await _lldbProcess?.stdinWriteln('breakpoint command add --script-type python $breakpointId');
241276
await _lldbProcess?.stdinWriteln(_pythonScript);
242277
await _lldbProcess?.stdinWriteln('DONE');
243-
244-
// Disable asynchronous mode to workaround issues with rearming of breakpoints.
245-
// See https://github.com/flutter/flutter/issues/184254 and upstream issue
246-
// https://github.com/llvm/llvm-project/issues/190956.
247-
await _lldbProcess?.stdinWriteln('script lldb.debugger.SetAsync(False)');
248278
}
249279

250280
/// Resume the stopped process.

packages/flutter_tools/lib/src/macos/xcdevice.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,7 @@ class XCDevice {
649649
xcodeDebug: _xcodeDebug,
650650
fileSystem: globals.fs,
651651
processUtils: _processUtils,
652+
xcode: _xcode,
652653
),
653654
xcodeDebug: _xcodeDebug,
654655
platform: globals.platform,

packages/flutter_tools/test/general.shard/ios/core_devices_test.dart

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ void main() {
7878
final processUtils = ProcessUtils(processManager: processManager, logger: logger);
7979
final fakeLLDB = FakeLLDB();
8080
final launcher = IOSCoreDeviceLauncher(
81+
xcode: FakeXcode(),
8182
coreDeviceControl: fakeCoreDeviceControl,
8283
logger: logger,
8384
xcodeDebug: FakeXcodeDebug(),
@@ -104,6 +105,7 @@ void main() {
104105
final logger = BufferLogger.test();
105106
final processUtils = ProcessUtils(processManager: processManager, logger: logger);
106107
final launcher = IOSCoreDeviceLauncher(
108+
xcode: FakeXcode(),
107109
coreDeviceControl: fakeCoreDeviceControl,
108110
logger: logger,
109111
xcodeDebug: FakeXcodeDebug(),
@@ -132,6 +134,7 @@ void main() {
132134
final logger = BufferLogger.test();
133135
final processUtils = ProcessUtils(processManager: processManager, logger: logger);
134136
final launcher = IOSCoreDeviceLauncher(
137+
xcode: FakeXcode(),
135138
coreDeviceControl: fakeCoreDeviceControl,
136139
logger: logger,
137140
xcodeDebug: FakeXcodeDebug(),
@@ -156,6 +159,7 @@ void main() {
156159
final logger = BufferLogger.test();
157160
final processUtils = ProcessUtils(processManager: processManager, logger: logger);
158161
final launcher = IOSCoreDeviceLauncher(
162+
xcode: FakeXcode(),
159163
coreDeviceControl: fakeCoreDeviceControl,
160164
logger: logger,
161165
xcodeDebug: FakeXcodeDebug(),
@@ -203,6 +207,7 @@ void main() {
203207
final processUtils = ProcessUtils(processManager: processManager, logger: logger);
204208
final fakeLLDB = FakeLLDB();
205209
final launcher = IOSCoreDeviceLauncher(
210+
xcode: FakeXcode(),
206211
coreDeviceControl: fakeCoreDeviceControl,
207212
logger: logger,
208213
xcodeDebug: FakeXcodeDebug(),
@@ -261,6 +266,7 @@ void main() {
261266
final processUtils = ProcessUtils(processManager: processManager, logger: logger);
262267
final fakeLLDB = FakeLLDB();
263268
final launcher = IOSCoreDeviceLauncher(
269+
xcode: FakeXcode(),
264270
coreDeviceControl: fakeCoreDeviceControl,
265271
logger: logger,
266272
xcodeDebug: FakeXcodeDebug(),
@@ -312,6 +318,7 @@ void main() {
312318
final processUtils = ProcessUtils(processManager: processManager, logger: logger);
313319
final fakeLLDB = FakeLLDB();
314320
final launcher = IOSCoreDeviceLauncher(
321+
xcode: FakeXcode(),
315322
coreDeviceControl: fakeCoreDeviceControl,
316323
logger: logger,
317324
xcodeDebug: FakeXcodeDebug(),
@@ -356,6 +363,7 @@ void main() {
356363
final processUtils = ProcessUtils(processManager: processManager, logger: logger);
357364
final fakeLLDB = FakeLLDB();
358365
final launcher = IOSCoreDeviceLauncher(
366+
xcode: FakeXcode(),
359367
coreDeviceControl: fakeCoreDeviceControl,
360368
logger: logger,
361369
xcodeDebug: FakeXcodeDebug(),
@@ -406,6 +414,7 @@ void main() {
406414
final processUtils = ProcessUtils(processManager: processManager, logger: logger);
407415
final fakeLLDB = FakeLLDB();
408416
final launcher = IOSCoreDeviceLauncher(
417+
xcode: FakeXcode(),
409418
coreDeviceControl: fakeCoreDeviceControl,
410419
logger: logger,
411420
xcodeDebug: FakeXcodeDebug(),
@@ -450,6 +459,7 @@ void main() {
450459
final processUtils = ProcessUtils(processManager: processManager, logger: logger);
451460
final fakeLLDB = FakeLLDB();
452461
final launcher = IOSCoreDeviceLauncher(
462+
xcode: FakeXcode(),
453463
coreDeviceControl: fakeCoreDeviceControl,
454464
logger: logger,
455465
xcodeDebug: FakeXcodeDebug(),
@@ -496,6 +506,7 @@ void main() {
496506
final processUtils = ProcessUtils(processManager: processManager, logger: logger);
497507
final fakeLLDB = FakeLLDB();
498508
final launcher = IOSCoreDeviceLauncher(
509+
xcode: FakeXcode(),
499510
coreDeviceControl: fakeCoreDeviceControl,
500511
logger: logger,
501512
xcodeDebug: FakeXcodeDebug(),
@@ -544,6 +555,7 @@ void main() {
544555
final processUtils = ProcessUtils(processManager: processManager, logger: logger);
545556
final fakeLLDB = FakeLLDB(attachSuccess: false);
546557
final launcher = IOSCoreDeviceLauncher(
558+
xcode: FakeXcode(),
547559
coreDeviceControl: fakeCoreDeviceControl,
548560
logger: logger,
549561
xcodeDebug: FakeXcodeDebug(),
@@ -579,6 +591,7 @@ void main() {
579591
);
580592

581593
final launcher = IOSCoreDeviceLauncher(
594+
xcode: FakeXcode(),
582595
coreDeviceControl: FakeIOSCoreDeviceControl(),
583596
logger: logger,
584597
xcodeDebug: fakeXcodeDebug,
@@ -612,6 +625,7 @@ void main() {
612625
);
613626

614627
final launcher = IOSCoreDeviceLauncher(
628+
xcode: FakeXcode(),
615629
coreDeviceControl: FakeIOSCoreDeviceControl(),
616630
logger: logger,
617631
xcodeDebug: fakeXcodeDebug,
@@ -645,6 +659,7 @@ void main() {
645659
);
646660

647661
final launcher = IOSCoreDeviceLauncher(
662+
xcode: FakeXcode(),
648663
coreDeviceControl: FakeIOSCoreDeviceControl(),
649664
logger: logger,
650665
xcodeDebug: fakeXcodeDebug,
@@ -678,6 +693,7 @@ void main() {
678693
);
679694

680695
final launcher = IOSCoreDeviceLauncher(
696+
xcode: FakeXcode(),
681697
coreDeviceControl: FakeIOSCoreDeviceControl(),
682698
logger: logger,
683699
xcodeDebug: fakeXcodeDebug,
@@ -710,6 +726,7 @@ void main() {
710726
final processUtils = ProcessUtils(processManager: processManager, logger: logger);
711727
final fakeLLDB = FakeLLDB();
712728
final launcher = IOSCoreDeviceLauncher(
729+
xcode: FakeXcode(),
713730
coreDeviceControl: fakeCoreDeviceControl,
714731
logger: logger,
715732
xcodeDebug: xcodeDebug,
@@ -739,6 +756,7 @@ void main() {
739756
final processUtils = ProcessUtils(processManager: processManager, logger: logger);
740757

741758
final launcher = IOSCoreDeviceLauncher(
759+
xcode: FakeXcode(),
742760
coreDeviceControl: fakeCoreDeviceControl,
743761
logger: logger,
744762
xcodeDebug: xcodeDebug,
@@ -767,6 +785,7 @@ void main() {
767785
final processUtils = ProcessUtils(processManager: processManager, logger: logger);
768786

769787
final launcher = IOSCoreDeviceLauncher(
788+
xcode: FakeXcode(),
770789
coreDeviceControl: fakeCoreDeviceControl,
771790
logger: logger,
772791
xcodeDebug: xcodeDebug,
@@ -794,6 +813,7 @@ void main() {
794813
final processUtils = ProcessUtils(processManager: processManager, logger: logger);
795814

796815
final launcher = IOSCoreDeviceLauncher(
816+
xcode: FakeXcode(),
797817
coreDeviceControl: fakeCoreDeviceControl,
798818
logger: logger,
799819
xcodeDebug: xcodeDebug,
@@ -4137,3 +4157,10 @@ class FakeShutdownHooks extends Fake implements ShutdownHooks {
41374157
_isShuttingDown = true;
41384158
}
41394159
}
4160+
4161+
class FakeXcode extends Fake implements Xcode {
4162+
FakeXcode({Version? currentVersion}) : currentVersion = currentVersion ?? Version(15, 0, 0);
4163+
4164+
@override
4165+
final Version currentVersion;
4166+
}

0 commit comments

Comments
 (0)