Skip to content

Commit d64b8e2

Browse files
Show Linux driver information in flutter doctor (#163980)
I have attempted to cherry pick some useful information from eglinfo which is often requested when resolving issues with rendering. This information is not required to compile an Flutter application, so it is a little different to the other displayed information.
1 parent b287365 commit d64b8e2

File tree

3 files changed

+546
-0
lines changed

3 files changed

+546
-0
lines changed

packages/flutter_tools/lib/src/base/user_messages.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,9 @@ class UserMessages {
275275
String get gtkLibrariesMissing =>
276276
'GTK 3.0 development libraries are required for Linux development.\n'
277277
'They are likely available from your distribution (e.g.: apt install libgtk-3-dev)';
278+
String get eglinfoMissing =>
279+
"Unable to access driver information using 'eglinfo'.\n"
280+
'It is likely available from your distribution (e.g.: apt install mesa-utils)';
278281

279282
// Messages used in FlutterCommand
280283
String flutterElapsedTime(String name, String elapsedTime) =>

packages/flutter_tools/lib/src/linux/linux_doctor.dart

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import 'package:process/process.dart';
77
import '../base/io.dart';
88
import '../base/user_messages.dart';
99
import '../base/version.dart';
10+
import '../convert.dart';
1011
import '../doctor_validator.dart';
1112

1213
/// A combination of version description and parsed version number.
@@ -29,6 +30,87 @@ class _VersionInfo {
2930
Version? number;
3031
}
3132

33+
/// Information about graphics drivers.
34+
class _DriverInformation {
35+
_DriverInformation({required ProcessManager processManager}) : _processManager = processManager;
36+
37+
final ProcessManager _processManager;
38+
List<List<String>> _sections = <List<String>>[];
39+
40+
Future<bool> load() async {
41+
ProcessResult? result;
42+
try {
43+
result = await _processManager.run(<String>['eglinfo'], stdoutEncoding: utf8);
44+
} on ArgumentError {
45+
// ignore error.
46+
}
47+
if (result == null || result.exitCode != 0) {
48+
return false;
49+
}
50+
51+
// Break into sections separated by an empty line.
52+
final List<String> lines = (result.stdout as String).split('\n');
53+
final List<List<String>> sections = <List<String>>[<String>[]];
54+
for (final String line in lines) {
55+
if (line == '') {
56+
if (sections.last.isNotEmpty) {
57+
sections.add(<String>[]);
58+
}
59+
} else {
60+
sections.last.add(line);
61+
}
62+
}
63+
if (sections.last.isEmpty) {
64+
sections.removeLast();
65+
}
66+
_sections = sections;
67+
68+
return true;
69+
}
70+
71+
List<String>? _getSection(String sectionName) {
72+
for (final List<String> lines in _sections) {
73+
if (lines[0] == '$sectionName:') {
74+
return lines;
75+
}
76+
}
77+
78+
return null;
79+
}
80+
81+
// Extracts a variable from eglinfo output.
82+
String? getVariable(String sectionName, String name) {
83+
final List<String>? lines = _getSection(sectionName);
84+
if (lines == null) {
85+
return null;
86+
}
87+
88+
final String prefix = '$name:';
89+
for (int i = 0; i < lines.length; i++) {
90+
if (lines[i].startsWith(prefix)) {
91+
String value = lines[i].substring(prefix.length).trim();
92+
// Combine multi-line indented values.
93+
if (value == '') {
94+
for (int j = i + 1; j < lines.length && lines[j].startsWith(' '); j++) {
95+
if (value == '') {
96+
value += ' ';
97+
}
98+
value += lines[j].trim();
99+
}
100+
}
101+
return value;
102+
}
103+
}
104+
105+
return null;
106+
}
107+
108+
// Extracts a comma separated list variable.
109+
List<String>? getListVariable(String sectionName, String name) {
110+
return getVariable(sectionName, name)?.split(',').map((String s) => s.trim()).toList();
111+
}
112+
}
113+
32114
/// A validator that checks for Clang and Make build dependencies.
33115
class LinuxDoctorValidator extends DoctorValidator {
34116
LinuxDoctorValidator({required ProcessManager processManager, required UserMessages userMessages})
@@ -160,6 +242,94 @@ class LinuxDoctorValidator extends DoctorValidator {
160242
}
161243
}
162244

245+
// Messages for drivers.
246+
{
247+
final _DriverInformation driverInfo = _DriverInformation(processManager: _processManager);
248+
if (!await driverInfo.load()) {
249+
messages.add(ValidationMessage.hint(_userMessages.eglinfoMissing));
250+
} else {
251+
const String kWaylandPlatform = 'Wayland platform';
252+
const String kX11Platform = 'X11 platform';
253+
const String kOpenGLCoreProfileRenderer = 'OpenGL core profile renderer';
254+
const String kOpenGLCoreProfileShadingLanguageVersion =
255+
'OpenGL core profile shading language version';
256+
const String kOpenGLCoreProfileVersion = 'OpenGL core profile version';
257+
const String kOpenGLCoreProfileExtensions = 'OpenGL core profile extensions';
258+
const String kOpenGLESProfileRenderer = 'OpenGL ES profile renderer';
259+
const String kOpenGLESProfileVersion = 'OpenGL ES profile version';
260+
const String kOpenGLESProfileShadingLanguageVersion =
261+
'OpenGL ES profile shading language version';
262+
const String kOpenGLESProfileExtensions = 'OpenGL ES profile extensions';
263+
264+
// Check both Wayland and X11 platforms for value.
265+
String? getPlatformVariable(String name) {
266+
final String? waylandValue = driverInfo.getVariable(kWaylandPlatform, name);
267+
final String? x11Value = driverInfo.getVariable(kX11Platform, name);
268+
if (waylandValue == null && x11Value == null) {
269+
return null;
270+
}
271+
272+
if (waylandValue == null) {
273+
return '$x11Value (X11)';
274+
} else if (x11Value == null) {
275+
return '$waylandValue (Wayland)';
276+
} else if (waylandValue == x11Value) {
277+
return waylandValue;
278+
} else {
279+
return '$waylandValue (Wayland) $x11Value (X11)';
280+
}
281+
}
282+
283+
// Check if has specified OpenGL extension.
284+
ValidationMessage extensionStatus(String variableName, String extensionName) {
285+
final List<String> waylandExtensions =
286+
driverInfo.getListVariable(kWaylandPlatform, variableName) ?? <String>[];
287+
final List<String> x11Extensions =
288+
driverInfo.getListVariable(kX11Platform, variableName) ?? <String>[];
289+
290+
final bool hasWayland = waylandExtensions.contains(extensionName);
291+
final bool hasX11 = x11Extensions.contains(extensionName);
292+
String status;
293+
if (!hasWayland && !hasX11) {
294+
status = 'no';
295+
} else if (!hasWayland) {
296+
status = 'yes (X11)';
297+
} else if (!hasX11) {
298+
status = 'yes (Wayland)';
299+
} else {
300+
status = 'yes';
301+
}
302+
303+
return ValidationMessage('$extensionName: $status');
304+
}
305+
306+
final String? renderer = getPlatformVariable(kOpenGLCoreProfileRenderer);
307+
if (renderer != null) {
308+
messages.add(ValidationMessage('OpenGL core renderer: $renderer'));
309+
final String version = getPlatformVariable(kOpenGLCoreProfileVersion) ?? 'unknown';
310+
messages.add(ValidationMessage('OpenGL core version: $version'));
311+
final String shadingLanguageVersion =
312+
getPlatformVariable(kOpenGLCoreProfileShadingLanguageVersion) ?? 'unknown';
313+
messages.add(
314+
ValidationMessage('OpenGL core shading language version: $shadingLanguageVersion'),
315+
);
316+
}
317+
final String? esRenderer = getPlatformVariable(kOpenGLESProfileRenderer);
318+
if (esRenderer != null) {
319+
messages.add(ValidationMessage('OpenGL ES renderer: $esRenderer'));
320+
final String version = getPlatformVariable(kOpenGLESProfileVersion) ?? 'unknown';
321+
messages.add(ValidationMessage('OpenGL ES version: $version'));
322+
final String shadingLanguageVersion =
323+
getPlatformVariable(kOpenGLESProfileShadingLanguageVersion) ?? 'unknown';
324+
messages.add(
325+
ValidationMessage('OpenGL ES shading language version: $shadingLanguageVersion'),
326+
);
327+
}
328+
messages.add(extensionStatus(kOpenGLCoreProfileExtensions, 'GL_EXT_framebuffer_blit'));
329+
messages.add(extensionStatus(kOpenGLESProfileExtensions, 'GL_EXT_texture_format_BGRA8888'));
330+
}
331+
}
332+
163333
return ValidationResult(validationType, messages);
164334
}
165335

0 commit comments

Comments
 (0)