Skip to content

Commit

Permalink
Improve handling of gdb commands
Browse files Browse the repository at this point in the history
Instead of waiting till the gdb output matches the expected command or
timeouts, wait for the command to complete (or timeout), i.e. for the
prompt to appear. This way if a command prints the expected output but
doesn't complete, it's execution time is not being counted by the next
`waitForBufferToMatch` invocation, additionally if the command completes
but doesn't return the expected result we don't have to wait for the
timeout. Furthermore, we can enhance the logging to see if the command
timed out or completed but without matching the expected output.

To ensure the prompt appears consistently across gdb versions after
every command we use the GDB/MI mode, i.e. the "--interpreter=mi"
option.

Closes Karm#173
  • Loading branch information
zakkak committed Jul 26, 2023
1 parent 5eace8d commit ac40ff0
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ public void debugSymbolsSmokeGDB(TestInfo testInfo) throws IOException, Interrup
// We should somehow capture this semantically in an Enum or something. This is fragile...
builderRoutine(app.buildAndRunCmds.cmds.length - 2, app, report, cn, mn, appDir, processLog, null, switches);

final ProcessBuilder processBuilder = new ProcessBuilder(getRunCommand("gdb", "./target/debug-symbols-smoke"));
final ProcessBuilder processBuilder = new ProcessBuilder(getRunCommand("gdb", "--interpreter=mi", "./target/debug-symbols-smoke"));
final Map<String, String> envA = processBuilder.environment();
envA.put("PATH", System.getenv("PATH"));
processBuilder.directory(appDir)
Expand All @@ -148,8 +148,7 @@ public void debugSymbolsSmokeGDB(TestInfo testInfo) throws IOException, Interrup
try (BufferedReader r = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = r.readLine()) != null) {
stringBuffer.append(line);
stringBuffer.append('\n');
stringBuffer.append(filterAndUnescapeGDBMIOutput(line));
}
} catch (IOException e) {
e.printStackTrace();
Expand Down Expand Up @@ -206,7 +205,7 @@ public void debugSymbolsQuarkus(TestInfo testInfo) throws IOException, Interrupt
processLog = Path.of(appDir.getAbsolutePath(), "logs", "build-and-run.log").toFile();
builderRoutine(app.buildAndRunCmds.cmds.length - 1, app, report, cn, mn, appDir, processLog);

final ProcessBuilder processBuilder = new ProcessBuilder(getRunCommand("gdb", "./target/quarkus-runner"));
final ProcessBuilder processBuilder = new ProcessBuilder(getRunCommand("gdb", "--interpreter=mi", "./target/quarkus-runner"));
final Map<String, String> envA = processBuilder.environment();
envA.put("PATH", System.getenv("PATH"));
processBuilder.directory(appDir)
Expand All @@ -219,8 +218,7 @@ public void debugSymbolsQuarkus(TestInfo testInfo) throws IOException, Interrupt
try (BufferedReader r = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = r.readLine()) != null) {
stringBuffer.append(line);
stringBuffer.append('\n');
stringBuffer.append(filterAndUnescapeGDBMIOutput(line));
}
} catch (IOException e) {
e.printStackTrace();
Expand Down Expand Up @@ -315,7 +313,7 @@ public void debugSymbolsQuarkusContainer(TestInfo testInfo) throws IOException,
// Q 1.11.7.Final and Mandrel 20.3.2 needs work/application.debug
// Is Q 2.x baking debug symbols to the main executable too?
final ProcessBuilder processBuilder = new ProcessBuilder(getRunCommand(
CONTAINER_RUNTIME, "exec", "-i", ContainerNames.QUARKUS_BUILDER_IMAGE_ENCODING.name, "/usr/bin/gdb", "/work/application.debug", "1"))
CONTAINER_RUNTIME, "exec", "-i", ContainerNames.QUARKUS_BUILDER_IMAGE_ENCODING.name, "/usr/bin/gdb", "--interpreter=mi", "/work/application.debug", "1"))
.directory(appDir)
.redirectErrorStream(true);
final Map<String, String> envA = processBuilder.environment();
Expand All @@ -328,8 +326,7 @@ public void debugSymbolsQuarkusContainer(TestInfo testInfo) throws IOException,
try (BufferedReader r = new BufferedReader(new InputStreamReader(gdbProcess.getInputStream()))) {
String line;
while ((line = r.readLine()) != null) {
stringBuffer.append(line);
stringBuffer.append('\n');
stringBuffer.append(filterAndUnescapeGDBMIOutput(line));
}
} catch (IOException e) {
e.printStackTrace();
Expand Down Expand Up @@ -424,4 +421,28 @@ public static void carryOutGDBSession(StringBuffer stringBuffer, GDBSession gdbS
"Note that commands in the session might depend on each other. Errors: " +
System.lineSeparator() + String.join(", " + System.lineSeparator(), errorQueue));
}

private static String filterAndUnescapeGDBMIOutput(String line) {
switch (line.charAt(0)) {
case '&':
// Strip & prefix added by GDB/MI to gdb input
case '=':
// Strip = prefix added by GDB/MI to program output
line = line.substring(1);
break;
case '~':
// Strip ~ prefix and quotes added by GDB/MI
line = line.substring(2, line.length() - 1);
break;
default:
break;
}
// Replace \n with newlines
line = line.replace("\\n", System.lineSeparator());
// Replace \" with "
line = line.replace("\\\"", "\"");
// Replace \t with tab
line = line.replace("\\t", "\t");
return line;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -905,8 +905,11 @@ public static boolean waitForBufferToMatch(StringBuffer stringBuffer, Pattern pa
long sleepMillis = unit.toMillis(sleep);
long startMillis = System.currentTimeMillis();
while (System.currentTimeMillis() - startMillis < timeoutMillis) {
if (pattern.matcher(stringBuffer.toString()).matches()) {
return true;
// Wait for command to complete, i.e. for the prompt to appear.
// To ensure the prompt appears consistently across gdb versions after every command we use the GDB/MI mode, i.e. the "--interpreter=mi" option.
// See https://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI-Output-Syntax.html#GDB_002fMI-Output-Syntax
if (Pattern.compile(".*\\(gdb\\).*", Pattern.DOTALL).matcher(stringBuffer.toString()).matches()) {
return pattern.matcher(stringBuffer.toString()).matches();
}
try {
Thread.sleep(sleepMillis);
Expand Down

0 comments on commit ac40ff0

Please sign in to comment.